297 lines
11 KiB
Vala
297 lines
11 KiB
Vala
/*
|
||
Copyright (C) 2007-2013 Christian Dywan <christian@twotoasts.de>
|
||
Copyright (C) 2009 Jean-François Guchens <zcx000@gmail.com>
|
||
Copyright (C) 2011 Peter Hatina <phatina@redhat.com>
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Lesser General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 2.1 of the License, or (at your option) any later version.
|
||
|
||
See the file COPYING for the full license text.
|
||
*/
|
||
|
||
namespace Midori {
|
||
public enum NewView {
|
||
TAB,
|
||
BACKGROUND,
|
||
WINDOW,
|
||
}
|
||
/* Since: 0.1.2 */
|
||
|
||
public enum Security {
|
||
NONE, /* The connection is neither encrypted nor verified. */
|
||
UNKNOWN, /* The security is unknown, due to lack of validation. */
|
||
TRUSTED /* The security is validated and trusted. */
|
||
}
|
||
/* Since: 0.2.5 */
|
||
|
||
[CCode (cprefix = "MIDORI_LOAD_")]
|
||
public enum LoadStatus {
|
||
FINISHED, /* The current website is fully loaded. */
|
||
COMMITTED, /* Data is being loaded and rendered. */
|
||
PROVISIONAL /* A new URI was scheduled. */
|
||
}
|
||
|
||
[CCode (cprefix = "MIDORI_LOAD_ERROR_")]
|
||
public enum LoadError {
|
||
NONE,
|
||
DELAYED,
|
||
SECURITY,
|
||
CRASH,
|
||
NETWORK
|
||
}
|
||
|
||
public class Tab : Gtk.VBox {
|
||
public Tab related { get; set construct; }
|
||
public WebKit.WebView web_view { get; private set; }
|
||
|
||
private string current_uri = "about:blank";
|
||
public string uri { get {
|
||
return current_uri;
|
||
}
|
||
protected set {
|
||
current_uri = Midori.URI.format_for_display (value);
|
||
}
|
||
}
|
||
|
||
/* Special is an error, blank or delayed page */
|
||
public bool special { get; protected set; default = false; }
|
||
/* Minimizing a tab indicates that only the icon should be shown.
|
||
Since: 0.1.8 */
|
||
public bool minimized { get; set; default = false; }
|
||
/* Since: 0.4.8 */
|
||
public string mime_type { get; protected set; default = "text/plain"; }
|
||
/* Since: 0.1.2 */
|
||
public Security security { get; protected set; default = Security.NONE; }
|
||
public LoadStatus load_status { get; protected set; default = LoadStatus.FINISHED; }
|
||
public LoadError load_error { get; protected set; default = LoadError.NONE; }
|
||
public string? statusbar_text { get; protected set; default = null; }
|
||
/* Since: 0.5.0 */
|
||
|
||
public Gdk.Color? fg_color { get; protected set; default = null; }
|
||
private Gdk.Color? bg_color_ = null;
|
||
public Gdk.Color? bg_color { get {
|
||
return bg_color_;
|
||
} protected set {
|
||
bg_color_ = value;
|
||
colors_changed ();
|
||
} }
|
||
/* After fg_color and bg_color have changed.
|
||
Since: 0.5.7 */
|
||
public signal void colors_changed ();
|
||
|
||
/* Special pages don't convey progress */
|
||
private double current_progress = 0.0;
|
||
public double progress { get {
|
||
return special ? 0.0 : current_progress;
|
||
}
|
||
protected set {
|
||
/* When we are finished, we don't want to *see* progress anymore */
|
||
if (load_status == LoadStatus.FINISHED)
|
||
current_progress = 0.0;
|
||
/* Full progress but not finished: presumably all loaded */
|
||
else if (value == 1.0)
|
||
current_progress = 0.0;
|
||
/* When loading we want to see at minimum 10% progress */
|
||
else
|
||
current_progress = value.clamp (0.1, 1.0);
|
||
}
|
||
}
|
||
|
||
/* Emitted when a uri is attempted to be loaded.
|
||
Returns FALSE if the URI could not be handled by Midori or any
|
||
external application.
|
||
Since: 0.5.8
|
||
*/
|
||
public signal bool open_uri (string uri);
|
||
/* Since: 0.5.8 */
|
||
public signal bool navigation_requested (string uri);
|
||
public signal void console_message (string message, int line, string source_id);
|
||
public signal void attach_inspector (WebKit.WebView inspector_view);
|
||
/* Emitted when an open inspector that was previously
|
||
attached to the window is now detached again.
|
||
Since: 0.3.4
|
||
*/
|
||
public signal void detach_inspector (WebKit.WebView inspector_view);
|
||
/* Allow the browser to provide the find bar */
|
||
public signal void search_text (bool found, string typing);
|
||
|
||
/* Since: 0.5.5 */
|
||
public signal void context_menu (WebKit.HitTestResult hit_test_result, ContextAction menu);
|
||
|
||
/* A dialog tab has a fixed size, limited GUI and is transient.
|
||
Since: 0.5.6 */
|
||
public bool is_dialog { get; protected set; }
|
||
|
||
public bool is_blank () {
|
||
return URI.is_blank (uri);
|
||
}
|
||
|
||
construct {
|
||
#if HAVE_GTK3
|
||
orientation = Gtk.Orientation.VERTICAL;
|
||
#endif
|
||
|
||
#if HAVE_WEBKIT2_3_91
|
||
web_view = related != null ?
|
||
new WebKit.WebView.with_related_view (related.web_view) : new WebKit.WebView ();
|
||
#else
|
||
web_view = new WebKit.WebView ();
|
||
#endif
|
||
/* Load something to avoid a bug where WebKit might not set a main frame */
|
||
web_view.load_uri ("");
|
||
}
|
||
|
||
public void inject_stylesheet (string stylesheet) {
|
||
#if !HAVE_WEBKIT2
|
||
var dom = web_view.get_dom_document ();
|
||
return_if_fail (dom.head != null);
|
||
try {
|
||
var style = dom.create_element ("style");
|
||
style.set_attribute ("type", "text/css");
|
||
style.append_child (dom.create_text_node (stylesheet));
|
||
dom.head.append_child (style);
|
||
}
|
||
catch (Error error) {
|
||
critical (_("Failed to inject stylesheet: %s"), error.message);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/* Since: 0.5.1
|
||
Deprecated: 0.6.0: The feature is no longer provided by WebKit.
|
||
*/
|
||
public bool view_source { get; private set; default = false; }
|
||
|
||
public bool can_view_source () {
|
||
if (view_source)
|
||
return false;
|
||
string content_type = ContentType.from_mime_type (mime_type);
|
||
#if HAVE_WIN32
|
||
/* On Win32 text/plain maps to ".txt" but is_a expects "text" */
|
||
string text_type = "text";
|
||
#else
|
||
string text_type = ContentType.from_mime_type ("text/plain");
|
||
#endif
|
||
return ContentType.is_a (content_type, text_type);
|
||
}
|
||
|
||
public static string get_display_title (string? title, string uri) {
|
||
/* Render filename as title of patches */
|
||
if (title == null && (uri.has_suffix (".diff") || uri.has_suffix (".patch")))
|
||
return File.new_for_uri (uri).get_basename ();
|
||
|
||
/* Work-around libSoup not setting a proper directory title */
|
||
if (title == null || (title == "OMG!" && uri.has_prefix ("file://")))
|
||
return Midori.URI.strip_prefix_for_display (uri);
|
||
|
||
#if !HAVE_WIN32
|
||
/* If left-to-right text is combined with right-to-left text the default
|
||
behaviour of Pango can result in awkwardly aligned text. For example
|
||
"بستيان نوصر (hadess) | An era comes to an end - Midori" becomes
|
||
"hadess) | An era comes to an end - Midori) بستيان نوصر". So to prevent
|
||
this we insert an LRE character before the title which indicates that
|
||
we want left-to-right but retains the direction of right-to-left text. */
|
||
if (!title.has_prefix (""))
|
||
return "" + title;
|
||
#endif
|
||
return title;
|
||
}
|
||
|
||
public static Pango.EllipsizeMode get_display_ellipsize (string title, string uri) {
|
||
if (title == uri)
|
||
return Pango.EllipsizeMode.START;
|
||
|
||
if (title.has_suffix (".diff") || title.has_suffix (".patch"))
|
||
return Pango.EllipsizeMode.START;
|
||
|
||
string[] parts = title.split (" ");
|
||
if (parts[0] != null && uri.has_suffix (parts[parts.length - 1].down ()))
|
||
return Pango.EllipsizeMode.START;
|
||
|
||
return Pango.EllipsizeMode.END;
|
||
}
|
||
|
||
/* Since: 0.4.3 */
|
||
public bool can_save () {
|
||
if (is_blank () || special)
|
||
return false;
|
||
if (view_source)
|
||
return false;
|
||
#if !HAVE_WEBKIT2
|
||
if (web_view.get_main_frame ().get_data_source ().get_data () == null)
|
||
return false;
|
||
#endif
|
||
return true;
|
||
}
|
||
|
||
public void stop_loading () {
|
||
web_view.stop_loading ();
|
||
}
|
||
|
||
public bool can_go_forward () {
|
||
return web_view.can_go_forward ();
|
||
}
|
||
|
||
public void go_forward () {
|
||
web_view.go_forward ();
|
||
}
|
||
|
||
public void unmark_text_matches () {
|
||
#if !HAVE_WEBKIT2
|
||
web_view.unmark_text_matches ();
|
||
#endif
|
||
}
|
||
|
||
public bool find (string text, bool case_sensitive, bool forward) {
|
||
#if HAVE_WEBKIT2
|
||
var controller = web_view.get_find_controller ();
|
||
uint options = WebKit.FindOptions.WRAP_AROUND;
|
||
if (!case_sensitive)
|
||
options += WebKit.FindOptions.CASE_INSENSITIVE;
|
||
if (!forward)
|
||
options += WebKit.FindOptions.BACKWARDS;
|
||
controller.search (text, options, 0);
|
||
// FIXME: mark matches, count matches, not found
|
||
return true;
|
||
#else
|
||
bool found = false;
|
||
found = web_view.search_text (text, case_sensitive, forward, true);
|
||
web_view.mark_text_matches (text, case_sensitive, 0);
|
||
web_view.set_highlight_text_matches (true);
|
||
return found;
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
Updates all editing actions with regard to text selection.
|
||
|
||
Since: 0.5.8
|
||
*/
|
||
public async void update_actions (Gtk.ActionGroup actions) {
|
||
#if HAVE_WEBKIT2
|
||
try {
|
||
actions.get_action ("Undo").sensitive = yield web_view.can_execute_editing_command ("Undo", null);
|
||
actions.get_action ("Redo").sensitive = yield web_view.can_execute_editing_command ("Redo", null);
|
||
actions.get_action ("Cut").sensitive = yield web_view.can_execute_editing_command ("Cut", null);
|
||
actions.get_action ("Copy").sensitive = yield web_view.can_execute_editing_command ("Copy", null);
|
||
actions.get_action ("Paste").sensitive = yield web_view.can_execute_editing_command ("Paste", null);
|
||
actions.get_action ("Delete").sensitive = yield web_view.can_execute_editing_command ("Cut", null);
|
||
actions.get_action ("SelectAll").sensitive = yield web_view.can_execute_editing_command ("SelectAll", null);
|
||
} catch (Error error) {
|
||
critical ("Failed to update actions: %s", error.message);
|
||
}
|
||
#else
|
||
actions.get_action ("Undo").sensitive = web_view.can_undo ();
|
||
actions.get_action ("Redo").sensitive = web_view.can_redo ();
|
||
actions.get_action ("Cut").sensitive = web_view.can_cut_clipboard ();
|
||
actions.get_action ("Copy").sensitive = web_view.can_copy_clipboard ();
|
||
actions.get_action ("Paste").sensitive = web_view.can_paste_clipboard ();
|
||
actions.get_action ("Delete").sensitive = web_view.can_cut_clipboard ();
|
||
actions.get_action ("SelectAll").sensitive = true;
|
||
#endif
|
||
}
|
||
}
|
||
}
|