├── .gitignore ├── AUTHORS ├── README.md ├── data ├── meson.build └── vulcan.desktop.in ├── meson.build ├── misc └── screenshot.png └── src ├── config.vala ├── consts.vala ├── dynamiclist.vala ├── filebar.vala ├── fileinfo.vala ├── headerbar.vala ├── main.vala ├── meson.build ├── notabbox.vala ├── settingsbar.vala ├── sidebar.vala ├── sidebarlist.vala ├── sourcestack.vala ├── tabbox.vala ├── terminal.vala └── window.vala /.gitignore: -------------------------------------------------------------------------------- 1 | vulcan 2 | personal-notes.txt 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Ryan Sipes 2 | Barry Smith 3 | 4 | Special Credit to: 5 | donadigo 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: Vulcan is no longer under active development. PRs are still accepted. 2 | 3 | # Vulcan 4 | 5 | A minimalistic text editor designed for both ordinary use and software development. 6 | 7 | [![Click to view YouTube video](https://raw.githubusercontent.com/zesterer/vulcan/master/misc/screenshot.png)](https://www.youtube.com/watch?v=is2f3xVIvFM) 8 | 9 | ## What is Vulcan? 10 | 11 | Vulcan is the next incarnation of the Journal text editor (version 1.0.1 seen here: http://www.github.com/solus-project/journal/). Originally named JournalNext, we've since taken the decision to rename the project 'Vulcan' and make it independent from the Solus project. It aims to be fast, minimalist on first use, and provides the following basic features: 12 | 13 | We'd prefer Vulcan's development to be guided by it's users - you. If you have any suggestions for improvements, new features or you've found a bug, we strongly urge you to report it in the issues section. 14 | 15 | - Multi-file editing 16 | - Monospace text editing 17 | - Opening (multiple) plaintext files 18 | - Saving (including Save As) plaintext files 19 | - Edit detection using string hashing 20 | - Basic theming / configuration 21 | - Smooth, animated UI 22 | - Keyboard shortcuts for quick access to functionality 23 | 24 | However, in addition it also aims to provide a range of tools helpful to developers including: 25 | 26 | - Syntax highlighting 27 | - Highlight scheme theming 28 | - Built-in VTE Terminal 29 | - Line numbering 30 | 31 | ## Why not another mainstream text editor? 32 | 33 | Vulcan has both unique features and a unique emphasis on design. Widget elements are smoothly animated, and the interface is designed to be intuitive and self-explanatory. It contains many useful features for more advanced tasks, but emphasises simplicity and versatility. Vulcan's development is largely user-orientated. 34 | 35 | ## How can I build Vulcan? 36 | 37 | At the moment, Vulcan doesn't have a proper build system and uses a little BASH script for compilation. To compile Vulcan, execute these commands: 38 | 39 | ``` 40 | git clone https://github.com/zesterer/Vulcan 41 | 42 | meson build 43 | ninja -C build 44 | ninja -C build install 45 | ``` 46 | 47 | ## Dependencies 48 | 49 | Vulcan depends on various libraries: 50 | 51 | - Gtk 3.14 52 | - VTE 2.91 53 | - Pango 54 | - GtkSourceView 55 | - GLib 56 | - Gio 57 | - Vala 58 | 59 | ## Planned Features 60 | 61 | Please note that this list is subject to change. 62 | 63 | - Filesystem / source directory view 64 | - (Possible) integration with other build tools 65 | - Syntax completion 66 | - Pastebin / Hastebin integration 67 | - More sharing tools 68 | - Keyboard shortcuts 69 | - Restore files from previous session 70 | - Keyboard shortcuts 71 | - Drop-down language chooser 72 | 73 | ## User-Suggested (Potential) Wishlist 74 | 75 | - Return to popover settings? 76 | - Optional horizontal tab bar 77 | - Overview pane (a la sublime) 78 | - Option to hide all buttons and rely on shortcuts 79 | - Gedit-style open files system 80 | 81 | Got any more ideas? Suggest them in the issues section! 82 | 83 | ## Credits 84 | 85 | Vulcan is developed by Ryan Sipes & Barry Smith. 86 | All code within this repository is licensed under the GPL 2.0 software license unless otherwise specified. 87 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | i18n.merge_file( 2 | input: meson.project_name() + '.desktop.in', 3 | output: meson.project_name() + '.desktop', 4 | po_dir: join_paths(meson.source_root()), 5 | type: 'desktop', 6 | install: true, 7 | install_dir: join_paths(datadir, 'applications') 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /data/vulcan.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Vulcan 3 | Exec=vulcan 4 | Icon=text-editor 5 | Terminal=false 6 | Type=Application 7 | Categories=GTK;TextEditor; -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('vulcan', 'vala','c') 2 | 3 | i18n = import('i18n') 4 | 5 | add_project_arguments( 6 | '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), 7 | language: 'c' 8 | ) 9 | 10 | prefix = get_option('prefix') 11 | datadir = join_paths(prefix, get_option('datadir')) 12 | 13 | gtk_dep = dependency('gtk+-3.0') 14 | gtksourceview_dep = dependency('gtksourceview-3.0') 15 | pango_dep = dependency('pango') 16 | vte_dep = dependency('vte-2.91') 17 | 18 | subdir('data') 19 | subdir('src') 20 | 21 | #meson.add_install_script('meson/post_install.py') 22 | -------------------------------------------------------------------------------- /misc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zesterer/vulcan/4796d9c4ebc955c65dc2ec70dac163c8570309cc/misc/screenshot.png -------------------------------------------------------------------------------- /src/config.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class Config : Object 22 | { 23 | public Consts consts; 24 | 25 | public DynamicList properties; 26 | 27 | public signal void dataChanged(string name, string data); 28 | 29 | public Config(Consts consts) 30 | { 31 | this.consts = consts; 32 | this.properties = new DynamicList(); 33 | } 34 | 35 | public bool contains(string name) 36 | { 37 | for (int count = 0; count < this.properties.length; count ++) 38 | { 39 | if (this.properties[count].name == name) 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | 46 | public Property getPropertyObject(string name) 47 | { 48 | for (int count = 0; count < this.properties.length; count ++) 49 | { 50 | if (this.properties[count].name == name) 51 | return this.properties[count]; 52 | } 53 | 54 | return new Property("null", "null", this); 55 | } 56 | 57 | public void addProperty(string name, string data) 58 | { 59 | this.properties.add(new Property(name, data, this)); 60 | } 61 | 62 | public void setProperty(string name, string data) 63 | { 64 | if (this.contains(name)) 65 | { 66 | this.getPropertyObject(name).data = data; 67 | } 68 | else 69 | { 70 | this.addProperty(name, data); 71 | } 72 | 73 | this.consts.output("Set property " + name + " to " + data); 74 | } 75 | 76 | public string getProperty(string name) 77 | { 78 | if (this.contains(name)) 79 | { 80 | return this.getPropertyObject(name).data; 81 | } 82 | else 83 | { 84 | return "null"; 85 | } 86 | } 87 | } 88 | 89 | public class Property : Object 90 | { 91 | private string _name; 92 | private string _data; 93 | 94 | public Config mother; 95 | 96 | public Property(string name, string data, Config mother) 97 | { 98 | this._name = name; 99 | this.mother = mother; 100 | 101 | this.data = data; 102 | } 103 | 104 | public string name 105 | { 106 | get 107 | {return this._name;} 108 | } 109 | 110 | public string data 111 | { 112 | get 113 | {return this._data;} 114 | set 115 | { 116 | this._data = value; 117 | this.mother.dataChanged(this._name, value); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/consts.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class Consts : Object 22 | { 23 | public string name = "Vulcan"; 24 | public string smallname = "vulcan"; 25 | public string comment = "A Gtk app to edit your text files"; 26 | public int[] version = {0, 1, 0}; 27 | public string version_string; 28 | public string copyright = "Copyright \u00A9 2015 Barry Smith"; 29 | public string[] authors = {"Barry Smith "}; 30 | 31 | public int min_width = 600; 32 | public int min_height = 400; 33 | 34 | public int default_width = 800; 35 | public int default_height = 600; 36 | 37 | public Application application; 38 | 39 | public Consts(Application application) 40 | { 41 | this.application = application; 42 | 43 | this.version_string = @"$(this.version[0]).$(this.version[1])"; 44 | } 45 | 46 | public void output(string message, string type = "debug") 47 | { 48 | if ((bool.parse(this.application.config.getProperty("debug")) && type == "debug") || type != "debug") 49 | stdout.printf("[" + type.up() + "] " + message + "\n"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/dynamiclist.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | /* 20 | * This class here acts as a wrapper around GLib's built-in list type. 21 | * I didn't like the GLib.List API, so I built this wrapper to abstract it 22 | * behind a new interface that feels nicer to use when coding. It also makes 23 | * things a lot easier. 24 | */ 25 | 26 | public class DynamicList : GLib.Object 27 | { 28 | //The internal list. 'G' is the type that the list accepts (user-defined) 29 | private List _list = new List(); 30 | 31 | //Add an item to the list 32 | public void add(G data) 33 | { 34 | this._list.append(data); 35 | } 36 | 37 | //Remove a specific index of the list. 38 | public void remove_at(int index) 39 | { 40 | unowned List todelete = this._list.nth(index); 41 | this._list.delete_link(todelete); 42 | } 43 | 44 | //Remove a specific index of the list. 45 | public void remove(G data) 46 | { 47 | this._list.remove(data); 48 | } 49 | 50 | //Find the object at index like: G object = list[x]; 51 | public new G get(int index) 52 | { 53 | return this._list.nth_data(index); 54 | } 55 | 56 | //Same as above, except to set things like: list[x] = object; 57 | public new void set(int index, G data) 58 | { 59 | this._list.insert(data, index); 60 | this.remove_at(index + 1); 61 | } 62 | 63 | //Find the number of items in the list 64 | public int size() 65 | { 66 | return (int)this._list.length(); 67 | } 68 | 69 | //Find out whether the list contains a specific object. Better than looping 70 | public bool contains(G data) 71 | { 72 | for (int count = 0; count < this._list.length(); count ++) 73 | { 74 | if (this._list.nth_data(count) == data) 75 | return true; 76 | } 77 | 78 | return false; 79 | } 80 | 81 | //Find the list's length. Same as DynamicList.size(), but used for more. 82 | public int length 83 | { 84 | get 85 | {return (int)this._list.length();} 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/filebar.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class FileBar : Gtk.Box 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public Gtk.Button new_button; 28 | public Gtk.Button open_button; 29 | public Gtk.Button save_button; 30 | public Gtk.Button saveas_button; 31 | public Gtk.Button sidebar_button; 32 | 33 | public FileBar(Window mother) 34 | { 35 | this.root = mother.root; 36 | this.mother = mother; 37 | this.window = this.mother.window; 38 | 39 | this.set_margin_start(4); 40 | this.set_margin_end(4); 41 | this.set_margin_top(4); 42 | this.set_margin_bottom(4); 43 | 44 | this.orientation = Gtk.Orientation.VERTICAL; 45 | 46 | this.new_button = new Gtk.Button(); 47 | this.new_button.set_relief(Gtk.ReliefStyle.NONE); 48 | this.new_button.set_tooltip_text("Create a new file [Ctrl+N]"); 49 | this.new_button.set_image(new Gtk.Image.from_icon_name("document-new-symbolic", Gtk.IconSize.MENU)); 50 | this.new_button.clicked.connect(this.newFileButtonClicked); 51 | this.add(this.new_button); 52 | 53 | this.open_button = new Gtk.Button(); 54 | this.open_button.set_relief(Gtk.ReliefStyle.NONE); 55 | this.open_button.set_tooltip_text("Open an existing file [Ctrl+O]"); 56 | this.open_button.set_image(new Gtk.Image.from_icon_name("document-open-symbolic", Gtk.IconSize.MENU)); 57 | this.open_button.clicked.connect(this.openFileButtonClicked); 58 | this.add(this.open_button); 59 | 60 | this.save_button = new Gtk.Button(); 61 | this.save_button.set_relief(Gtk.ReliefStyle.NONE); 62 | this.save_button.set_tooltip_text("Save the current file [Ctrl+S]"); 63 | this.save_button.set_image(new Gtk.Image.from_icon_name("document-save-symbolic", Gtk.IconSize.MENU)); 64 | this.save_button.clicked.connect(this.saveFileButtonClicked); 65 | this.add(this.save_button); 66 | 67 | this.saveas_button = new Gtk.Button(); 68 | this.saveas_button.set_relief(Gtk.ReliefStyle.NONE); 69 | this.saveas_button.set_tooltip_text("Save the current file as a different file"); 70 | this.saveas_button.set_image(new Gtk.Image.from_icon_name("document-save-as-symbolic", Gtk.IconSize.MENU)); 71 | this.saveas_button.clicked.connect(this.saveAsFileButtonClicked); 72 | this.add(this.saveas_button); 73 | 74 | Gtk.Box spacer = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 75 | spacer.set_vexpand(true); 76 | this.add(spacer); 77 | 78 | this.sidebar_button = new Gtk.Button(); 79 | this.sidebar_button.set_relief(Gtk.ReliefStyle.NONE); 80 | this.sidebar_button.set_tooltip_text("Toggle the sidebar visibility [Ctrl+K]"); 81 | this.sidebar_button.clicked.connect(this.sideBarButtonClicked); 82 | this.sidebar_button.set_image(new Gtk.Image.from_icon_name("pane-hide-symbolic", Gtk.IconSize.MENU)); 83 | this.add(this.sidebar_button); 84 | } 85 | 86 | public void sideBarButtonClicked() 87 | { 88 | if (this.window.config.getProperty("show-sidebar") == "false") 89 | this.window.config.setProperty("show-sidebar", "true"); 90 | else 91 | this.window.config.setProperty("show-sidebar", "false"); 92 | } 93 | 94 | public void newFileButtonClicked() 95 | { 96 | this.window.newFile(); 97 | } 98 | 99 | public void openFileButtonClicked() 100 | { 101 | this.window.openFileWithDialog(); 102 | } 103 | 104 | public void saveFileButtonClicked() 105 | { 106 | this.root.consts.output("Saved button clicked"); 107 | if (this.window.source_stack.getCurrentTab() != null) 108 | this.window.source_stack.getCurrentTab().save(); 109 | } 110 | 111 | public void saveAsFileButtonClicked() 112 | { 113 | this.root.consts.output("Save As button clicked"); 114 | if (this.window.source_stack.getCurrentTab() != null) 115 | this.window.source_stack.getCurrentTab().save(true); 116 | } 117 | 118 | public void update() 119 | { 120 | if (this.window.source_stack.getCurrentTab() != null) 121 | { 122 | if (this.window.source_stack.getCurrentTab().unsaved) 123 | { 124 | this.save_button.override_background_color(Gtk.StateFlags.NORMAL, {1.0, 0.25, 0.0, 0.4}); 125 | this.save_button.set_tooltip_text("Save the current file (currently unsaved) [Ctrl+S]"); 126 | } 127 | else 128 | { 129 | this.save_button.override_background_color(Gtk.StateFlags.NORMAL, {0.0, 0.0, 0.0, 0.0}); 130 | this.save_button.set_tooltip_text("Save the current file"); 131 | } 132 | } 133 | else 134 | { 135 | this.root.consts.output("Source stack has a null tab currently"); 136 | this.save_button.override_background_color(Gtk.StateFlags.NORMAL, {0.0, 0.0, 0.0, 0.0}); 137 | } 138 | 139 | this.root.consts.output("Updated filebar"); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/fileinfo.vala: -------------------------------------------------------------------------------- 1 | namespace Vulcan 2 | { 3 | public class FileInfoPopover : Gtk.Popover 4 | { 5 | public Application root; 6 | public HeaderBar mother; 7 | public Window window; 8 | 9 | public Gtk.Box info_box; 10 | public Gtk.Label name_label; 11 | public Gtk.Label path_label; 12 | public Gtk.Label filesize_label; 13 | //public Gtk.Label modified_label; 14 | 15 | public FileInfo file_info; 16 | 17 | public FileInfoPopover(HeaderBar mother, Gtk.Widget button) 18 | { 19 | this.set_relative_to(button); 20 | 21 | this.root = mother.root; 22 | this.mother = mother; 23 | this.window = this.mother.window; 24 | 25 | this.info_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 4); 26 | this.info_box.set_margin_top(4); 27 | this.info_box.set_margin_bottom(4); 28 | this.info_box.set_margin_start(8); 29 | this.info_box.set_margin_end(8); 30 | this.add(this.info_box); 31 | 32 | this.name_label = new Gtk.Label("Name"); 33 | this.info_box.add(this.name_label); 34 | 35 | this.path_label = new Gtk.Label("Path"); 36 | this.info_box.add(this.path_label); 37 | 38 | this.filesize_label = new Gtk.Label("File Size"); 39 | this.info_box.add(this.filesize_label); 40 | 41 | //this.modified_label = new Gtk.Label("Modified"); 42 | //this.info_box.add(this.modified_label); 43 | } 44 | 45 | public void toggleVisible() 46 | { 47 | if (this.get_visible()) 48 | { 49 | this.hide(); 50 | } 51 | else 52 | { 53 | this.show_all(); 54 | } 55 | } 56 | 57 | public void update() 58 | { 59 | if (this.window.source_stack.getCurrentTab() != null) 60 | { 61 | this.file_info = this.window.source_stack.getCurrentTab().file.query_info("standard::size", 0); 62 | this.name_label.set_markup(this.window.source_stack.getCurrentTab().filename); 63 | this.path_label.set_markup(this.window.source_stack.getCurrentTab().file.get_path()); 64 | this.filesize_label.set_markup(this.file_info.get_size().to_string() + " bytes"); 65 | //this.modified_label.set_markup(this.file_info.get_modification_time().to_iso8601()); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/headerbar.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class HeaderBar : Gtk.HeaderBar 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public Gtk.Box title_box; 28 | public Gtk.Button title_button; 29 | public FileInfoPopover title_popover; 30 | public Gtk.Button syntax_button; 31 | 32 | public Gtk.Button new_window_button; 33 | public Gtk.ToggleButton settings_button; 34 | 35 | public HeaderBar(Window mother) 36 | { 37 | this.root = mother.root; 38 | this.mother = mother; 39 | this.window = this.mother.window; 40 | 41 | this.set_subtitle(this.root.consts.comment); 42 | this.set_show_close_button(true); 43 | 44 | this.title_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); 45 | this.title_box.height_request = 32; 46 | this.title_box.get_style_context().add_class("linked"); 47 | this.set_custom_title(this.title_box); 48 | 49 | this.title_button = new Gtk.Button(); 50 | this.title_box.add(this.title_button); 51 | 52 | this.title_popover = new FileInfoPopover(this, this.title_button); 53 | this.title_button.clicked.connect(this.title_popover.toggleVisible); 54 | 55 | this.syntax_button = new Gtk.Button(); 56 | this.title_box.add(this.syntax_button); 57 | 58 | this.setTitle(this.root.consts.name); 59 | this.setSyntax("None"); 60 | 61 | this.new_window_button = new Gtk.Button(); 62 | this.new_window_button.set_image(new Gtk.Image.from_icon_name("text-editor-symbolic", Gtk.IconSize.MENU)); 63 | this.new_window_button.clicked.connect(this.newWindowButtonClicked); 64 | this.new_window_button.set_tooltip_text("Open a new window [Ctrl+W]"); 65 | this.pack_start(this.new_window_button); 66 | 67 | this.settings_button = new Gtk.ToggleButton(); 68 | this.settings_button.set_tooltip_text("Toggle the settings bar visibility [Ctrl+P]"); 69 | this.settings_button.set_image(new Gtk.Image.from_icon_name("open-menu-symbolic", Gtk.IconSize.MENU)); 70 | this.settings_button.clicked.connect(this.settingsBarButtonClicked); 71 | this.pack_end(this.settings_button); 72 | 73 | this.window.config.dataChanged.connect(this.dataWindowChanged); 74 | } 75 | 76 | public void dataWindowChanged(string name, string data) 77 | { 78 | switch (name) 79 | { 80 | case ("show-settingsbar"): 81 | this.settings_button.set_active(bool.parse(data)); 82 | break; 83 | } 84 | } 85 | 86 | public void settingsBarButtonClicked() 87 | { 88 | this.window.config.setProperty("show-settingsbar", this.settings_button.get_active().to_string()); 89 | } 90 | public void newWindowButtonClicked() 91 | { 92 | this.root.addWindow(); 93 | } 94 | 95 | public void setTitle(string title) 96 | { 97 | this.title_button.set_label(title); 98 | } 99 | 100 | public void setSyntax(string syntax) 101 | { 102 | this.syntax_button.set_label(syntax); 103 | } 104 | 105 | public void setSubtitle(string subtitle) 106 | { 107 | this.title_button.set_label(subtitle); 108 | } 109 | 110 | public void tabSwitched() 111 | { 112 | TabBox? tab = this.window.source_stack.getCurrentTab(); 113 | 114 | if (tab == null) 115 | { 116 | this.setTitle(this.root.consts.name); 117 | this.setSyntax("None"); 118 | this.set_subtitle(this.root.consts.comment); 119 | } 120 | else 121 | { 122 | string title = tab.filename; 123 | string syntax = "None"; 124 | if (tab.language != null) 125 | syntax = tab.language; 126 | 127 | this.setTitle(title); 128 | this.setSyntax(syntax); 129 | if (tab.file == null) 130 | this.set_subtitle("Unknown location"); 131 | else 132 | this.set_subtitle(tab.file.get_path()); 133 | } 134 | 135 | this.title_popover.update(); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class Application : Gtk.Application 22 | { 23 | public Application root; 24 | 25 | public Consts consts; 26 | public Config config; 27 | 28 | public DynamicList windows; 29 | 30 | public Application(string[] args) 31 | { 32 | this.root = this; 33 | 34 | this.root.consts.output("Starting application..."); 35 | 36 | this.consts = new Consts(this); 37 | this.config = new Config(this.consts); 38 | this.config.dataChanged.connect(this.dataApplicationChanged); 39 | this.config.setProperty("debug", "true"); 40 | this.config.setProperty("dark-theme", "true"); 41 | 42 | this.windows = new DynamicList(); 43 | 44 | this.addWindow(); 45 | } 46 | 47 | public Window addWindow() 48 | { 49 | Window window = new Window(this); 50 | this.windows.add(window); 51 | return window; 52 | } 53 | 54 | public void dataApplicationChanged(string name, string data) 55 | { 56 | switch (name) 57 | { 58 | case ("dark-theme"): 59 | Gtk.Settings.get_default().set("gtk-application-prefer-dark-theme", bool.parse(data)); 60 | break; 61 | } 62 | } 63 | 64 | public void close() 65 | { 66 | if (this.windows.length < 1) 67 | { 68 | this.consts.output(@"There are $(this.windows.length) windows left"); 69 | this.root.consts.output("Closing application..."); 70 | Gtk.main_quit(); 71 | } 72 | } 73 | } 74 | 75 | int main(string[] args) 76 | { 77 | Gtk.init(ref args); 78 | 79 | Application application; 80 | application = new Application(args); 81 | 82 | Gtk.main(); 83 | 84 | return 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | sources = [ 2 | 'main.vala', 3 | 'window.vala', 4 | 'headerbar.vala', 5 | 'consts.vala', 6 | 'sourcestack.vala', 7 | 'tabbox.vala', 8 | 'dynamiclist.vala', 9 | 'sidebar.vala', 10 | 'filebar.vala', 11 | 'settingsbar.vala', 12 | 'terminal.vala', 13 | 'sidebarlist.vala', 14 | 'config.vala', 15 | 'notabbox.vala', 16 | 'fileinfo.vala' 17 | ] 18 | 19 | executable( 20 | meson.project_name(), 21 | sources, 22 | dependencies: [gtksourceview_dep,gtk_dep,pango_dep,vte_dep], 23 | install: true, 24 | install_dir: join_paths(prefix, 'bin') 25 | ) 26 | -------------------------------------------------------------------------------- /src/notabbox.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class NoTabBox : Gtk.Box 22 | { 23 | public Application root; 24 | public SourceStack mother; 25 | public Window window; 26 | 27 | public Gtk.Label label; 28 | public Gtk.Box button_box; 29 | public Gtk.Button new_button; 30 | public Gtk.Button open_button; 31 | 32 | public NoTabBox(SourceStack mother) 33 | { 34 | this.root = mother.root; 35 | this.mother = mother; 36 | this.window = this.mother.window; 37 | 38 | this.set_orientation(Gtk.Orientation.VERTICAL); 39 | this.set_homogeneous(true); 40 | 41 | this.label = new Gtk.Label(""); 42 | this.label.set_justify(Gtk.Justification.CENTER); 43 | this.label.set_markup("Welcome to " + this.root.consts.name + " " + this.root.consts.version_string + "\n\nNo files are currently open"); 44 | this.label.set_hexpand(true); 45 | //this.label.set_vexpand(true); 46 | this.add(this.label); 47 | 48 | this.button_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 16); 49 | this.button_box.set_homogeneous(true); 50 | this.add(this.button_box); 51 | 52 | this.new_button = new Gtk.Button(); 53 | this.new_button.set_halign(Gtk.Align.CENTER); 54 | this.new_button.set_valign(Gtk.Align.CENTER); 55 | this.new_button.set_relief(Gtk.ReliefStyle.NONE); 56 | this.new_button.override_background_color(Gtk.StateFlags.NORMAL, {0.5, 0.5, 0.5, 0.5}); 57 | this.new_button.width_request = 100; 58 | this.new_button.height_request = 100; 59 | this.new_button.set_tooltip_text("Create a new file"); 60 | this.new_button.set_image(new Gtk.Image.from_icon_name("document-new-symbolic", Gtk.IconSize.DIALOG)); 61 | this.new_button.clicked.connect(this.window.filebar.newFileButtonClicked); 62 | this.button_box.add(this.new_button); 63 | 64 | this.open_button = new Gtk.Button(); 65 | this.open_button.set_halign(Gtk.Align.CENTER); 66 | this.open_button.set_valign(Gtk.Align.CENTER); 67 | this.open_button.set_relief(Gtk.ReliefStyle.NONE); 68 | this.open_button.override_background_color(Gtk.StateFlags.NORMAL, {0.5, 0.5, 0.5, 0.5}); 69 | this.open_button.width_request = 100; 70 | this.open_button.height_request = 100; 71 | this.open_button.set_tooltip_text("Open an existing file"); 72 | this.open_button.set_image(new Gtk.Image.from_icon_name("document-open-symbolic", Gtk.IconSize.DIALOG)); 73 | this.open_button.clicked.connect(this.window.filebar.openFileButtonClicked); 74 | this.button_box.add(this.open_button); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/settingsbar.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class SettingsBar : Gtk.Box 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public Gtk.ScrolledWindow scrolled_window; 28 | public Gtk.ListBox list_box; 29 | 30 | public Gtk.Label label; 31 | public DarkThemeSwitchRow dark_switch_row; 32 | public LineNumbersSwitchRow line_switch_row; 33 | public TextWrapSwitchRow text_wrap_row; 34 | public SourceSchemeRow source_scheme_row; 35 | public ShowTerminalSwitchRow terminal_switch_row; 36 | 37 | public Gtk.Box bottom_box; 38 | public Gtk.Button about_button; 39 | 40 | public SettingsBar(Window mother) 41 | { 42 | this.root = mother.root; 43 | this.mother = mother; 44 | this.window = this.mother.window; 45 | 46 | this.set_orientation(Gtk.Orientation.VERTICAL); 47 | this.set_vexpand(true); 48 | 49 | this.scrolled_window = new Gtk.ScrolledWindow(null, null); 50 | this.scrolled_window.set_vexpand(true); 51 | this.scrolled_window.width_request = 240; 52 | this.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); 53 | //this.scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_OUT); 54 | this.add(this.scrolled_window); 55 | 56 | this.list_box = new Gtk.ListBox(); 57 | this.list_box.set_selection_mode(Gtk.SelectionMode.NONE); 58 | this.scrolled_window.add(this.list_box); 59 | 60 | this.label = new Gtk.Label(""); 61 | this.label.set_justify(Gtk.Justification.CENTER); 62 | this.label.set_markup("Settings"); 63 | this.label.set_hexpand(true); 64 | this.label.set_margin_top(16); 65 | this.label.set_margin_bottom(16); 66 | this.list_box.add(this.label); 67 | 68 | this.dark_switch_row = new DarkThemeSwitchRow(this); 69 | this.list_box.add(this.dark_switch_row); 70 | 71 | this.line_switch_row = new LineNumbersSwitchRow(this); 72 | this.list_box.add(this.line_switch_row); 73 | 74 | this.text_wrap_row = new TextWrapSwitchRow(this); 75 | this.list_box.add(this.text_wrap_row); 76 | 77 | this.source_scheme_row = new SourceSchemeRow(this); 78 | this.list_box.add(this.source_scheme_row); 79 | 80 | this.terminal_switch_row = new ShowTerminalSwitchRow(this); 81 | this.list_box.add(this.terminal_switch_row); 82 | 83 | this.bottom_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 4); 84 | this.add(this.bottom_box); 85 | 86 | this.about_button = new Gtk.Button(); 87 | this.about_button.set_image(new Gtk.Image.from_icon_name("dialog-information-symbolic", Gtk.IconSize.MENU)); 88 | this.about_button.set_tooltip_text("About"); 89 | this.about_button.clicked.connect(this.showAboutDialog); 90 | this.bottom_box.pack_start(this.about_button); 91 | } 92 | 93 | public void showAboutDialog() 94 | { 95 | Gtk.show_about_dialog(this.window, 96 | "program-name", this.root.consts.name, 97 | "copyright", this.root.consts.copyright, 98 | "license-type", Gtk.License.GPL_2_0, 99 | "comments", this.root.consts.comment, 100 | "version", this.root.consts.version_string, 101 | "logo-icon-name", this.root.consts.smallname, 102 | "authors", this.root.consts.authors); 103 | } 104 | } 105 | 106 | public class SettingsRowGeneric : Gtk.Box 107 | { 108 | public Application root; 109 | public SettingsBar mother; 110 | public Window window; 111 | 112 | public SettingsRowGeneric(SettingsBar mother) 113 | { 114 | this.root = mother.root; 115 | this.mother = mother; 116 | this.window = this.mother.window; 117 | 118 | this.orientation = Gtk.Orientation.HORIZONTAL; 119 | this.set_margin_start(16); 120 | this.set_margin_end(16); 121 | this.set_margin_top(4); 122 | this.set_margin_bottom(4); 123 | this.set_spacing(8); 124 | } 125 | } 126 | 127 | public class DarkThemeSwitchRow : SettingsRowGeneric 128 | { 129 | public Gtk.Label label; 130 | public Gtk.Switch switcher; 131 | 132 | public DarkThemeSwitchRow(SettingsBar mother) 133 | { 134 | base(mother); 135 | 136 | this.set_tooltip_text("Switch between the dark and light theme variants"); 137 | 138 | this.label = new Gtk.Label("Dark Theme"); 139 | this.add(this.label); 140 | 141 | this.switcher = new Gtk.Switch(); 142 | this.switcher.set_halign(Gtk.Align.END); 143 | this.switcher.set_state(bool.parse(this.root.config.getProperty("dark-theme"))); 144 | this.switcher.state_set.connect(this.setState); 145 | this.pack_end(this.switcher); 146 | } 147 | 148 | public bool setState(bool state) 149 | { 150 | this.root.config.setProperty("dark-theme", state.to_string()); 151 | return true; 152 | } 153 | } 154 | 155 | public class LineNumbersSwitchRow : SettingsRowGeneric 156 | { 157 | public Gtk.Label label; 158 | public Gtk.Switch switcher; 159 | 160 | public LineNumbersSwitchRow(SettingsBar mother) 161 | { 162 | base(mother); 163 | 164 | this.set_tooltip_text("Toggle displaying line numbers"); 165 | 166 | this.label = new Gtk.Label("Line Numbers"); 167 | this.add(this.label); 168 | 169 | this.switcher = new Gtk.Switch(); 170 | this.switcher.set_halign(Gtk.Align.END); 171 | this.switcher.set_state(bool.parse(this.window.config.getProperty("line-numbers"))); 172 | this.switcher.state_set.connect(this.setState); 173 | this.pack_end(this.switcher); 174 | } 175 | 176 | public bool setState(bool state) 177 | { 178 | this.window.config.setProperty("line-numbers", state.to_string()); 179 | return true; 180 | } 181 | } 182 | 183 | public class SourceSchemeRow : SettingsRowGeneric 184 | { 185 | public Gtk.Label label; 186 | public Gtk.ComboBoxText combo_box; 187 | 188 | public SourceSchemeRow(SettingsBar mother) 189 | { 190 | base(mother); 191 | 192 | this.set_tooltip_text("Choose which scheme text in the editor should be themed with"); 193 | 194 | this.label = new Gtk.Label("Text Scheme"); 195 | this.add(this.label); 196 | 197 | this.combo_box = new Gtk.ComboBoxText(); 198 | this.combo_box.set_halign(Gtk.Align.END); 199 | 200 | //Find all the currently available schemes 201 | Gtk.SourceStyleSchemeManager scheme_manager = Gtk.SourceStyleSchemeManager.get_default(); 202 | string[] schemes = scheme_manager.get_scheme_ids(); 203 | for (int count = 0; count < schemes.length; count ++) 204 | { 205 | this.combo_box.append_text(schemes[count]); 206 | if (this.window.config.getProperty("scheme") == schemes[count]) 207 | this.combo_box.set_active(count); 208 | } 209 | 210 | this.combo_box.changed.connect(this.changeScheme); 211 | this.pack_end(this.combo_box); 212 | } 213 | 214 | public void changeScheme() 215 | { 216 | this.window.config.setProperty("scheme", this.combo_box.get_active_text()); 217 | } 218 | } 219 | 220 | public class ShowTerminalSwitchRow : SettingsRowGeneric 221 | { 222 | public Gtk.Label label; 223 | public Gtk.Switch switcher; 224 | 225 | public ShowTerminalSwitchRow(SettingsBar mother) 226 | { 227 | base(mother); 228 | 229 | this.set_tooltip_text("Toggle VTE Terminal visibility"); 230 | 231 | this.label = new Gtk.Label("Show Terminal"); 232 | this.add(this.label); 233 | 234 | this.switcher = new Gtk.Switch(); 235 | this.switcher.set_halign(Gtk.Align.END); 236 | this.switcher.set_state(bool.parse(this.window.config.getProperty("show-terminal"))); 237 | this.switcher.state_set.connect(this.setState); 238 | this.pack_end(this.switcher); 239 | } 240 | 241 | public bool setState(bool state) 242 | { 243 | this.window.config.setProperty("show-terminal", state.to_string()); 244 | return true; 245 | } 246 | } 247 | 248 | public class TextWrapSwitchRow : SettingsRowGeneric 249 | { 250 | public Gtk.Label label; 251 | public Gtk.Switch switcher; 252 | 253 | public TextWrapSwitchRow(SettingsBar mother) 254 | { 255 | base(mother); 256 | 257 | this.set_tooltip_text("Toggle text wrapping over lines"); 258 | 259 | this.label = new Gtk.Label("Text Wrapping"); 260 | this.add(this.label); 261 | 262 | this.switcher = new Gtk.Switch(); 263 | this.switcher.set_halign(Gtk.Align.END); 264 | this.switcher.set_state(bool.parse(this.window.config.getProperty("text-wrap"))); 265 | this.switcher.state_set.connect(this.setState); 266 | this.pack_end(this.switcher); 267 | } 268 | 269 | public bool setState(bool state) 270 | { 271 | this.window.config.setProperty("text-wrap", state.to_string()); 272 | return true; 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/sidebar.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class SideBar : Gtk.Stack 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public SideBarList sidebar_list; 28 | 29 | public SideBar(Window mother) 30 | { 31 | this.root = mother.root; 32 | this.mother = mother; 33 | this.window = this.mother.window; 34 | 35 | this.width_request = 150; 36 | 37 | this.sidebar_list = new SideBarList(this); 38 | this.add(this.sidebar_list); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/sidebarlist.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class SideBarList : Gtk.Box 22 | { 23 | public Application root; 24 | public SideBar mother; 25 | public Window window; 26 | 27 | public Gtk.ScrolledWindow scrolled_window; 28 | public Gtk.ListBox list_box; 29 | 30 | public SideBarList(SideBar mother) 31 | { 32 | this.root = mother.root; 33 | this.mother = mother; 34 | this.window = this.mother.window; 35 | 36 | this.set_vexpand(true); 37 | 38 | this.scrolled_window = new Gtk.ScrolledWindow(null, null); 39 | this.scrolled_window.width_request = 180; 40 | this.add(this.scrolled_window); 41 | 42 | this.list_box = new Gtk.ListBox(); 43 | this.list_box.set_selection_mode(Gtk.SelectionMode.SINGLE); 44 | this.scrolled_window.add(this.list_box); 45 | } 46 | } 47 | 48 | public class SideBarTabRow : Gtk.Box 49 | { 50 | public Application root; 51 | public SideBarList mother; 52 | public Window window; 53 | public unowned TabBox owner; 54 | 55 | public Gtk.Image icon; 56 | public Gtk.Label label; 57 | public Gtk.Button close_button; 58 | 59 | public SideBarTabRow(SideBarList mother, TabBox owner) 60 | { 61 | this.root = mother.root; 62 | this.mother = mother; 63 | this.window = this.mother.window; 64 | this.owner = owner; 65 | 66 | this.set_orientation(Gtk.Orientation.HORIZONTAL); 67 | this.set_margin_start(8); 68 | this.set_margin_end(8); 69 | this.set_spacing(4); 70 | this.set_tooltip_text(this.owner.filename); 71 | 72 | this.icon = new Gtk.Image(); 73 | this.icon.set_from_icon_name("text-x-generic-symbolic", Gtk.IconSize.MENU); 74 | this.add(this.icon); 75 | 76 | this.label = new Gtk.Label("null"); 77 | this.label.set_ellipsize(Pango.EllipsizeMode.END); 78 | this.add(this.label); 79 | 80 | this.close_button = new Gtk.Button(); 81 | this.close_button.set_halign(Gtk.Align.END); 82 | this.close_button.set_relief(Gtk.ReliefStyle.NONE); 83 | this.close_button.override_background_color(Gtk.StateFlags.NORMAL, {1.0, 1.0, 1.0, 0.05}); 84 | this.close_button.set_image(new Gtk.Image.from_icon_name("window-close-symbolic", Gtk.IconSize.BUTTON)); 85 | this.close_button.clicked.connect(this.closeClicked); 86 | this.pack_end(this.close_button); 87 | 88 | this.mother.list_box.add(this); 89 | this.show_all(); 90 | 91 | this.mother.list_box.row_selected.connect(this.clickSelected); 92 | this.owner.unsavedChanged.connect(this.unsavedChanged); 93 | } 94 | 95 | public void unsavedChanged() 96 | { 97 | 98 | } 99 | 100 | public void closeClicked() 101 | { 102 | this.owner.close(); 103 | } 104 | 105 | public void update() 106 | { 107 | this.label.set_label(this.owner.filename); 108 | } 109 | 110 | public void switchTo() 111 | { 112 | this.window.source_stack.switchTo(this.owner); 113 | } 114 | 115 | public void clickSelected(Gtk.ListBoxRow? row) 116 | { 117 | if (row.get_child() == this) 118 | { 119 | this.switchTo(); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/sourcestack.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class SourceStack : Gtk.Stack 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public DynamicList tabs; 28 | 29 | public NoTabBox no_tab_box; 30 | 31 | public signal void hasSwitched(); 32 | 33 | public SourceStack(Window mother) 34 | { 35 | this.root = mother.root; 36 | this.mother = mother; 37 | this.window = this.mother.window; 38 | 39 | this.set_vexpand(true); 40 | this.set_hexpand(true); 41 | this.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN); 42 | this.set_transition_duration(500); 43 | 44 | this.hasSwitched.connect(this.window.filebar.update); 45 | 46 | this.tabs = new DynamicList(); 47 | 48 | //The default page 49 | this.no_tab_box = new NoTabBox(this); 50 | this.add(this.no_tab_box); 51 | this.switchTo(this.no_tab_box); 52 | 53 | this.remove.connect(this.checkRemove); 54 | } 55 | 56 | public TabBox addTab(File? file) 57 | { 58 | TabBox tab = new TabBox(this, file); 59 | 60 | this.tabs.add(tab); 61 | 62 | this.add(tab); 63 | 64 | this.switchTo(tab); 65 | 66 | if (this.tabs.length == 1) 67 | this.remove(this.no_tab_box); 68 | 69 | if (this.tabs.length == 1) 70 | this.window.config.setProperty("show-sidebar", "true"); 71 | 72 | return tab; 73 | } 74 | 75 | public void switchTo(Gtk.Widget widget) 76 | { 77 | if (widget != this.no_tab_box) 78 | this.root.consts.output("Switching to tab with file " + ((TabBox)widget).filename); 79 | this.set_visible_child(widget); 80 | if (widget != this.no_tab_box) 81 | this.window.sidebar.sidebar_list.list_box.select_row((Gtk.ListBoxRow)((TabBox)widget).sidebar_tab_row.parent); 82 | this.hasSwitched(); 83 | } 84 | 85 | public unowned TabBox? getCurrentTab() 86 | { 87 | if (this.get_visible_child() == this.no_tab_box) 88 | return null; 89 | 90 | return (TabBox)this.get_visible_child(); 91 | } 92 | 93 | public void checkRemove(Gtk.Widget widget) 94 | { 95 | if (this.tabs.length == 0) 96 | this.window.config.setProperty("show-sidebar", "false"); 97 | 98 | this.root.consts.output("Removed tab"); 99 | if (this.tabs.length == 0) 100 | { 101 | this.add(this.no_tab_box); 102 | this.switchTo(this.no_tab_box); 103 | } 104 | 105 | this.hasSwitched(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/tabbox.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class TabBox : Gtk.Box 22 | { 23 | public Application root; 24 | public SourceStack mother; 25 | public Window window; 26 | 27 | public Config config; 28 | 29 | //public Gtk.Overlay find_overlay; 30 | //public Gtk.Revealer find_revealer; 31 | 32 | public Gtk.ScrolledWindow scrolled_window; 33 | public Gtk.SourceView source_view; 34 | public Gtk.SourceBuffer text_buffer; 35 | public uint text_hash; 36 | public Gtk.SourceStyleSchemeManager source_style_scheme_manager; 37 | 38 | public SideBarTabRow sidebar_tab_row; 39 | 40 | public File? file; 41 | public string filename; 42 | public string language; 43 | 44 | private bool loaded; 45 | private bool _unsaved; 46 | public signal void unsavedChanged(); 47 | 48 | public TabBox(SourceStack mother, File? file) 49 | { 50 | this.loaded = false; 51 | 52 | this.root = mother.root; 53 | this.mother = mother; 54 | this.window = this.mother.window; 55 | 56 | this._unsaved = false; 57 | 58 | this.file = file; 59 | 60 | this.config = new Config(this.root.consts); 61 | 62 | 63 | 64 | this.scrolled_window = new Gtk.ScrolledWindow(null, null); 65 | this.scrolled_window.set_hexpand(true); 66 | this.scrolled_window.set_kinetic_scrolling(true); 67 | this.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); 68 | this.add(this.scrolled_window); 69 | 70 | this.source_view = new Gtk.SourceView(); 71 | if (bool.parse(this.window.config.getProperty("text-wrap"))) 72 | this.source_view.set_wrap_mode(Gtk.WrapMode.WORD); 73 | else 74 | this.source_view.set_wrap_mode(Gtk.WrapMode.NONE); 75 | this.source_view.editable = true; 76 | this.source_view.cursor_visible = true; 77 | this.source_view.set_show_line_numbers(bool.parse(this.window.config.getProperty("line-numbers"))); 78 | this.source_view.set_auto_indent(true); 79 | this.source_view.set_tab_width(4); 80 | var fontdec = new Pango.FontDescription(); 81 | fontdec.set_family("Monospace"); 82 | this.source_view.override_font(fontdec); 83 | 84 | this.text_buffer = new Gtk.SourceBuffer(null); 85 | this.text_buffer.changed.connect(this.change_buffer); 86 | this.source_view.set_buffer(this.text_buffer); 87 | 88 | if (this.file != null) 89 | { 90 | this.root.consts.output("Loading file " + this.filename); 91 | this.filename = this.file.get_basename(); 92 | 93 | string text; 94 | try 95 | { 96 | FileUtils.get_contents(this.file.get_path(), out text); 97 | this.setSyntaxHighlighting(); 98 | this.root.consts.output("Loaded text from file"); 99 | } 100 | catch (Error e) 101 | { 102 | text = ""; 103 | stderr.printf("Error: %s\n", e.message); 104 | } 105 | this.text_buffer.set_text(text); 106 | this.reHash(); 107 | } 108 | else 109 | { 110 | this.filename = "Untitled"; 111 | this.unsaved = true; 112 | } 113 | 114 | this.source_style_scheme_manager = Gtk.SourceStyleSchemeManager.get_default(); 115 | this.changeSourceScheme(this.window.config.getProperty("scheme")); 116 | this.text_buffer.set_highlight_syntax(true); 117 | 118 | this.scrolled_window.add(this.source_view); 119 | 120 | 121 | //Create it's sidebar tab row 122 | this.sidebar_tab_row = new SideBarTabRow(this.window.sidebar.sidebar_list, this); 123 | 124 | //Get any updates from the window's config 125 | this.window.config.dataChanged.connect(this.dataWindowChanged); 126 | this.unsavedChanged.connect(this.window.filebar.update); 127 | this.unsavedChanged(); 128 | 129 | this.show_all(); 130 | 131 | //Everything's finished and the new tab is loaded! 132 | this.loaded = true; 133 | this.update(); 134 | } 135 | 136 | public void reHash() 137 | { 138 | this.text_hash = text_buffer.text.hash(); 139 | this.unsaved = false; 140 | } 141 | 142 | public void update() 143 | { 144 | if (this.file != null) 145 | { 146 | this.filename = this.file.get_basename(); 147 | } 148 | 149 | this.sidebar_tab_row.update(); 150 | } 151 | 152 | public bool unsaved 153 | { 154 | get 155 | {return this._unsaved;} 156 | set 157 | { 158 | this._unsaved = value; 159 | this.unsavedChanged(); 160 | } 161 | } 162 | 163 | public void change_buffer() 164 | { 165 | if (this.loaded) 166 | { 167 | uint new_hash = text_buffer.text.hash(); 168 | this.unsaved = (new_hash != text_hash); 169 | } 170 | else 171 | { 172 | this.reHash(); 173 | } 174 | } 175 | 176 | public void changeSourceScheme(string scheme) 177 | { 178 | this.text_buffer.set_style_scheme(this.source_style_scheme_manager.get_scheme(scheme)); 179 | this.root.consts.output("Changed scheme for tab"); 180 | } 181 | 182 | public void dataWindowChanged(string name, string data) 183 | { 184 | switch (name) 185 | { 186 | case ("scheme"): 187 | this.changeSourceScheme(data); 188 | break; 189 | 190 | case ("line-numbers"): 191 | this.source_view.set_show_line_numbers(bool.parse(data)); 192 | break; 193 | 194 | case ("text-wrap"): 195 | { 196 | if (bool.parse(data)) 197 | this.source_view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR); 198 | else 199 | this.source_view.set_wrap_mode(Gtk.WrapMode.NONE); 200 | } 201 | break; 202 | } 203 | } 204 | 205 | public void setSyntaxHighlighting() 206 | { 207 | FileInfo? info = null; 208 | try 209 | { 210 | info = this.file.query_info("standard::*", FileQueryInfoFlags.NONE, null); 211 | } 212 | catch (Error e) 213 | { 214 | warning (e.message); 215 | return; 216 | } 217 | 218 | string mime_type = ContentType.get_mime_type (info.get_attribute_as_string (FileAttribute.STANDARD_CONTENT_TYPE)); 219 | 220 | Gtk.SourceLanguageManager language_manager = new Gtk.SourceLanguageManager(); 221 | language = language_manager.guess_language(this.file.get_path(), mime_type).get_name(); 222 | 223 | if (language != null) 224 | { 225 | this.text_buffer.set_language(language_manager.guess_language(file.get_path(), mime_type)); 226 | this.root.consts.output("Set language to " + language); 227 | } 228 | else 229 | { 230 | this.root.consts.output("No language set"); 231 | } 232 | } 233 | 234 | public void saveData() 235 | { 236 | try 237 | { 238 | FileUtils.set_contents(file.get_path(), this.text_buffer.text); 239 | this.reHash(); 240 | this.unsaved = false; 241 | 242 | this.root.consts.output("Saved"); 243 | } 244 | catch (Error error) 245 | { 246 | stderr.printf("Error: %s\n", error.message); 247 | } 248 | } 249 | 250 | public void save(bool saveas = false) 251 | { 252 | this.root.consts.output("Now saving..."); 253 | 254 | if (this.file != null && saveas == false) 255 | { 256 | this.saveData(); 257 | } 258 | else 259 | { 260 | this.root.consts.output("Getting new file choice..."); 261 | 262 | Gtk.FileChooserDialog file_chooser = new Gtk.FileChooserDialog("Save File", this.window, Gtk.FileChooserAction.SAVE, "Cancel", Gtk.ResponseType.CANCEL, "Save", Gtk.ResponseType.ACCEPT); 263 | 264 | file_chooser.set_local_only(true); 265 | file_chooser.set_do_overwrite_confirmation(true); 266 | file_chooser.set_create_folders(true); 267 | 268 | int response = file_chooser.run(); 269 | 270 | if (response == Gtk.ResponseType.ACCEPT) 271 | { 272 | this.file = File.new_for_path(file_chooser.get_filename()); 273 | this.update(); 274 | this.saveData(); 275 | } 276 | 277 | file_chooser.destroy(); 278 | } 279 | 280 | this.setSyntaxHighlighting(); 281 | this.window.source_stack.switchTo(this.window.source_stack.getCurrentTab()); 282 | } 283 | 284 | public bool canClose() 285 | { 286 | if (this.unsaved && this.text_buffer.text.length > 0) 287 | { 288 | string msg = "The file '" + this.filename + "' is not saved. Are you sure you wish to close without saving?"; 289 | Gtk.MessageDialog message_dialog = new Gtk.MessageDialog(this.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, msg); 290 | message_dialog.set_title("Unsaved File"); 291 | 292 | int response = message_dialog.run(); 293 | 294 | message_dialog.destroy(); 295 | 296 | if (response != Gtk.ResponseType.OK) 297 | { 298 | return false; 299 | } 300 | } 301 | 302 | return true; 303 | } 304 | 305 | public bool close(bool force = false) 306 | { 307 | bool can_close = this.canClose(); 308 | 309 | if (can_close || force) 310 | { 311 | this.sidebar_tab_row.mother.list_box.remove(this.sidebar_tab_row.parent); 312 | this.sidebar_tab_row.destroy(); 313 | this.mother.tabs.remove(this); 314 | this.destroy(); 315 | } 316 | 317 | return can_close; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/terminal.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class Terminal : Vte.Terminal 22 | { 23 | public Application root; 24 | public Window mother; 25 | public Window window; 26 | 27 | public int pid; 28 | 29 | public Terminal(Window mother) 30 | { 31 | this.root = mother.root; 32 | this.mother = mother; 33 | this.window = this.mother.window; 34 | 35 | this.set_hexpand(true); 36 | this.set_vexpand(false); 37 | 38 | try 39 | { 40 | this.spawn_sync(Vte.PtyFlags.DEFAULT, null, {Vte.get_user_shell()}, null, SpawnFlags.SEARCH_PATH, null, out this.pid); 41 | } 42 | catch (Error error) 43 | { 44 | this.root.consts.output(error.message); 45 | } 46 | 47 | Gdk.RGBA background = { 0.15, 0.15, 0.15, 1.0}; 48 | this.set_color_background(background); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/window.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Barry Smith 2 | * 3 | * This file is part of Vulcan. 4 | * 5 | * Vulcan is free software: you can redistribute it 6 | * and/or modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation, either version 2 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * Vulcan is distributed in the hope that it will be 11 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 13 | * Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with Vulcan. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | namespace Vulcan 20 | { 21 | public class Window : Gtk.Window 22 | { 23 | public Application root; 24 | public Application mother; 25 | public Window window; 26 | 27 | public Config config; 28 | 29 | public HeaderBar header_bar; 30 | 31 | public Gtk.Box main_box; 32 | public Gtk.Revealer sidebar_revealer; 33 | public SideBar sidebar; 34 | public FileBar filebar; 35 | public Gtk.Box centre_box; 36 | public Gtk.Overlay source_stack_overlay; 37 | public SourceStack source_stack; 38 | public Gtk.Revealer terminal_revealer; 39 | public Gtk.ScrolledWindow terminal_scrolled_window; 40 | public Terminal terminal; 41 | public Gtk.Revealer settingsbar_revealer; 42 | public SettingsBar settingsbar; 43 | 44 | public Window(Application mother) 45 | { 46 | this.root = mother.root; 47 | this.mother = mother; 48 | this.window = this; 49 | 50 | this.config = new Config(this.root.consts); 51 | this.config.setProperty("line-numbers", "true"); 52 | this.config.setProperty("text-wrap", "false"); 53 | this.config.setProperty("scheme", "solarized-dark"); 54 | this.config.setProperty("show-terminal", "false"); 55 | this.config.setProperty("show-sidebar", "false"); 56 | this.config.setProperty("show-settingsbar", "false"); 57 | 58 | this.set_size_request(this.root.consts.min_width, this.root.consts.min_height); 59 | this.set_default_size(this.root.consts.default_width, this.root.consts.default_height); 60 | this.destroy.connect(this.quitNow); 61 | 62 | this.header_bar = new HeaderBar(this); 63 | //this.add(this.header_bar); 64 | this.set_titlebar(this.header_bar); 65 | //this.header_bar.set_opacity(0.3f); 66 | //this.header_bar.set_vexpand(false); 67 | 68 | this.main_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); 69 | this.add(this.main_box); 70 | 71 | this.sidebar_revealer = new Gtk.Revealer(); 72 | this.sidebar_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_RIGHT); 73 | this.main_box.add(this.sidebar_revealer); 74 | 75 | this.sidebar = new SideBar(this); 76 | this.sidebar_revealer.add(this.sidebar); 77 | 78 | this.filebar = new FileBar(this); 79 | this.main_box.add(this.filebar); 80 | 81 | this.centre_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 82 | this.main_box.add(this.centre_box); 83 | 84 | this.source_stack_overlay = new Gtk.Overlay(); 85 | this.centre_box.add(this.source_stack_overlay); 86 | 87 | this.source_stack = new SourceStack(this); 88 | this.source_stack_overlay.add(this.source_stack); 89 | 90 | //Connect the headerbar 91 | this.source_stack.hasSwitched.connect(this.header_bar.tabSwitched); 92 | 93 | this.terminal_revealer = new Gtk.Revealer(); 94 | this.terminal_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_UP); 95 | this.centre_box.add(this.terminal_revealer); 96 | 97 | this.terminal_scrolled_window = new Gtk.ScrolledWindow(null, null); 98 | this.terminal_scrolled_window.height_request = 200; 99 | this.terminal_scrolled_window.set_hexpand(false); 100 | //this.terminal_scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_OUT); 101 | this.terminal_revealer.add(this.terminal_scrolled_window); 102 | 103 | this.terminal = new Terminal(this); 104 | this.terminal_scrolled_window.add(this.terminal); 105 | 106 | this.settingsbar_revealer = new Gtk.Revealer(); 107 | this.settingsbar_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_LEFT); 108 | this.settingsbar_revealer.set_halign(Gtk.Align.END); 109 | this.source_stack_overlay.add_overlay(this.settingsbar_revealer); 110 | 111 | this.settingsbar = new SettingsBar(this); 112 | this.settingsbar_revealer.add(this.settingsbar); 113 | 114 | this.config.dataChanged.connect(this.dataWindowChanged); 115 | 116 | //All created, now show it 117 | this.show_all(); 118 | 119 | this.key_press_event.connect(this.pressKey); 120 | 121 | } 122 | 123 | public void quitNow() 124 | { 125 | bool can_close = true; 126 | 127 | if (can_close) 128 | { 129 | this.root.windows.remove(this); 130 | this.root.close(); 131 | this.destroy(); 132 | } 133 | } 134 | 135 | public void showSideBar(bool show) 136 | { 137 | this.sidebar_revealer.set_reveal_child(show); 138 | 139 | //Used for switching the icon 140 | if (this.sidebar_revealer.get_child_revealed()) 141 | this.filebar.sidebar_button.set_image(new Gtk.Image.from_icon_name("pane-hide-symbolic", Gtk.IconSize.MENU)); 142 | else 143 | this.filebar.sidebar_button.set_image(new Gtk.Image.from_icon_name("pane-show-symbolic", Gtk.IconSize.MENU)); 144 | } 145 | 146 | public void dataWindowChanged(string name, string data) 147 | { 148 | switch (name) 149 | { 150 | case ("show-terminal"): 151 | this.terminal_revealer.set_reveal_child(bool.parse(data)); 152 | break; 153 | 154 | case ("show-settingsbar"): 155 | this.settingsbar_revealer.set_reveal_child(bool.parse(data)); 156 | break; 157 | 158 | case ("show-sidebar"): 159 | this.showSideBar(bool.parse(data)); 160 | break; 161 | } 162 | } 163 | 164 | public void openFileWithDialog() 165 | { 166 | Gtk.FileChooserDialog file_chooser = new Gtk.FileChooserDialog("Open File", this, Gtk.FileChooserAction.OPEN, "Cancel", Gtk.ResponseType.CANCEL, "Open", Gtk.ResponseType.ACCEPT); 167 | 168 | file_chooser.set_select_multiple(true); 169 | file_chooser.set_local_only(true); 170 | 171 | //Open the dialog 172 | int response = file_chooser.run(); 173 | 174 | if (response == Gtk.ResponseType.ACCEPT) 175 | { 176 | SList files_chosen = file_chooser.get_files(); 177 | for (int count = 0; count < files_chosen.length(); count ++) 178 | { 179 | this.root.consts.output("Opening file " + files_chosen.nth_data(count).get_path()); 180 | 181 | bool already_open = false; 182 | for (int count2 = 0; count2 < this.window.source_stack.tabs.length; count2 ++) 183 | { 184 | if (this.window.source_stack.tabs[count2].file.equal(files_chosen.nth_data(count))) 185 | { 186 | this.root.consts.output("File is already open!"); 187 | already_open = true; 188 | } 189 | } 190 | 191 | if (already_open == false) 192 | this.openFile(files_chosen.nth_data(count)); 193 | } 194 | 195 | this.root.consts.output("Opened " + files_chosen.length().to_string() + " files"); 196 | } 197 | 198 | file_chooser.destroy(); 199 | } 200 | 201 | public void openFile(File file) 202 | { 203 | TabBox tab = this.source_stack.addTab(file); 204 | tab.sidebar_tab_row.switchTo(); 205 | } 206 | 207 | public void newFile() 208 | { 209 | this.source_stack.addTab(null); 210 | } 211 | 212 | public bool pressKey(Gdk.EventKey key) 213 | { 214 | //Thanks donadigo. Saved me a few minutes reading documentation :-) 215 | if((key.state & Gdk.ModifierType.CONTROL_MASK) != 0) 216 | { 217 | switch (key.keyval) 218 | { 219 | case Gdk.Key.@q: 220 | this.quitNow(); 221 | break; 222 | case Gdk.Key.@n: 223 | this.newFile(); 224 | break; 225 | case Gdk.Key.@w: 226 | this.root.addWindow(); 227 | break; 228 | case Gdk.Key.@o: 229 | this.openFileWithDialog(); 230 | break; 231 | case Gdk.Key.@s: 232 | { 233 | //If shift is held, make it save as 234 | if (this.source_stack.getCurrentTab() != null) 235 | { 236 | this.source_stack.getCurrentTab().save(); 237 | } 238 | } 239 | break; 240 | case Gdk.Key.@k: 241 | { 242 | if (this.window.config.getProperty("show-sidebar") == "false") 243 | this.window.config.setProperty("show-sidebar", "true"); 244 | else 245 | this.window.config.setProperty("show-sidebar", "false"); 246 | } 247 | break; 248 | case Gdk.Key.@p: 249 | { 250 | if (this.window.config.getProperty("show-settingsbar") == "false") 251 | this.window.config.setProperty("show-settingsbar", "true"); 252 | else 253 | this.window.config.setProperty("show-settingsbar", "false"); 254 | } 255 | break; 256 | } 257 | } 258 | 259 | return false; 260 | } 261 | } 262 | } 263 | --------------------------------------------------------------------------------