Merge tag '0.5.2+dfsg'

Upstream version 0.5.2+dfsg
This commit is contained in:
Ryan Niebur 2013-10-23 20:45:02 -07:00
commit 7509079965
222 changed files with 108963 additions and 71711 deletions

2
.gitignore vendored
View file

@ -2,7 +2,7 @@ Makefile
.waf-* .waf-*
.lock-wscript .lock-wscript
_build_ _build
po/.intltool-merge-cache po/.intltool-merge-cache
po/LINGUAS po/LINGUAS

255
ChangeLog
View file

@ -1,5 +1,260 @@
This file is licensed under the terms of the expat license, see the file EXPAT. This file is licensed under the terms of the expat license, see the file EXPAT.
v0.5.2:
Re-release with a proper version number and changelog
v0.5.1:
Fix mouse gesture regression breaking context menu
Fix --run command line switch by midori_paths_init
Fix bug in size calculation for the history list popup
Handle diagnostic dialog argument in running instance
Fix feed panel default value crash
Ensure existence of the applications directory
Fix download tooltip crash and extend test case
Integrate user interaction exploit demo in about:
Don't convey loading or progress on special pages
Address missing NULL checks and dead code found by clang
No security window for blank pages, but a search icon
Introduce UI for created apps/ launchers: Web App Manager
Add custom-title setting to override browser title
Add a Gtk.Entry to --plain mode for entering URLs
Deprecate middle_click_opens_selection in favour of gtk-enable-primary-paste
Webkit2:
Require 1.11.91 aka 2.0.0 for WebKit2
Delayed load, clear favicons, clear HTTP cache, tab favicons
Navigation policy, mouse buttons, security details
basic cookies, download dialog, res://, stock://, print
Zoom, default-charset, view-source, spell-check, prefetch
Back/ forward, enable-java, plugin listing, web inspector
v0.5.0:
Store --execute arguments in string array
Prevent overlay frame from being caught by show_all
Unconditionally show Toolbar Style preference
Duplicate current URI when reloading Midori.View
Update tabs being closable on setting change in Granite
Check default_search before setting SearchAction default
Populate application chooser button in idle
Bail out of completion resizing if cell height is 0
Pass proxy to bookmark dialog when editing via menu
Tweak bookmark dialog, button to buttons, toggles side by side
Move 'Flash windows' option into History List
Use light window for Clear Private Data with Granite
Use GtkFontButton with filter func with GTK+ 3.2
Implement 'Run in debugger' button in diagnostic dialog
Add Win32 work-around to History List for modifiers
Make toolbar drag/ drop work in GTK+3
Check if active form element is input before getting search text
Implement direction-based mouse gesture configuration
Implement mouse movement, load-failed, crashed, search in WebKit2
Add 'Show last crash log' button to diagnostic dialog
Make invalid actions fail; exit on error in new process only
Accept setting=value and extension=true/ false in --execute
Merged cookie permissions as of 2013-03-08
Gray out webGL preference if context is unavailable
Use browser API to Close Other in view menu item
Fix periods to ellipsis in Custom/ Customize Shortcuts
Support Colorful Tabs in History List
Add Midori.Tab.fg/ bg_color and Midori.View.set_colors
Fix word-wrap, #decription and #message in about.css
Set view scroll policy to Never to avoid flickering
Use XDG_RUNTIME_DIR for temporary files
Build Vala and C parts of core separately
Don't provide default value for enable-scripts
Respect Open new pages: window for Web Search and Open Image
enable-javascript in WebKit1/ 2, macro for (Web)Settings
Fix MIDORI_*_VERSION to be integers
Fix .desktop file validation unit test and fix errors
'New tab behavior' preference: about:dial/ new/ search/ home alias URLs
Use stripped down XBEL variant for session and trash
Allow any proxies supported by libproxy; list supported types in preferences
v0.4.9:
Let non-Granite security window behave like a window
Disable Contractor support in Granite for now
Use cache_dir_for_reading in about:paths
Strip LRE to prevent it from begin saved to disk
Enable stripping 'referer' by default
Fix crash on closing Adlock preferences dialog
Bail on unset title in completion, fixing strchr urlbar crash
Manage cookies accept policy per domain - not installed by default
Don't store/ load stock:// icons for special pages
Drop KatzeScrolled in favour of GTK+ 3.4 touchscreen support
Write XBEL safely to prevent loss on eg. full disk
Omit nspluginwrapper Netscape plugins from extensions
Add --debug/ -g switch to run Midori in gdb
List versions from about:version in --version
Work in progress --enable-webkit2 option enabling WebKit2/ GTK+3
Rename menu _Window to _Tabs
Update Easylist subscription URL for Adblock
Stop redundant tab numbering when adding
Allow feed panel webview widget to shrink.
Don't search for place holder text on cookie list rebuild
Add 'Google Translate (gt)' as a search engine
Default external Download Manager to "fetch" on FreeBSD
Drop GCC-version specific -Wno-unused-but-set-variable
Change X-Ayatana-Desktop-Shortcuts to Actions
v0.4.8:
Fix un-delaying of tabs
Support downloads with FlashGet on Win32
Fix compilation with GLib 2.30
Fix error handling in extensions
Retain selection in urlbar when switching tabs
Fix missing right-click menu on NextForward button
Hide error page button if buttons have no images
Rework URL completion: suggest open tabs
Always highlight matches in inline search
Pantheon: Only show private launcher in search
Granite: Fix notebook, require 0.2, drop _about_dialog_new
Don't include http(s), file or www. in page title
Autodetect Twitter RSS feeds
Adblock: Improve date parsing
Unit test rework: backtraces, regardless of debugging, wine
More accurate version numbers in about:version
Drop obsolete --log-file command line switch
Emit inspector attach-window with correct signature
Fix Netscape plugins opening download dialogs
Rework path handling and setup in different modes (fix segfaults)
Manage Netscape plugins are individual extensions
Address gtk_icon_set_render_icon_pixbuf assertions
Fix renaming in speed dial with spaces in title
Render completion title/ URL side by side with Granite
Transparently use Favicon-/ IconDatabase/ file store per WebKit
Add TabMoveFirst/ Last hotkeys (without defaults)
Drop Hildon support
Show URI in 'not responding' dialog
Query search engine icons when loading, rather than stupid guesses
v0.4.7:
Unify download behavior: link fingerprints, space check, clearing, tooltips
GIO-based check for enough space and permissions, GIO-based themed icons
Show opener/ tab domain in download dialog:
http://lcamtuf.coredump.cx/fldl/ http://lcamtuf.coredump.cx/switch/
Extension to download with a specific command line
Size in download dialog and fallback filename heuristic
Windows: GTK+3, Faenza icons, gdb helper, Netscape plugins,
ship CA bundle, fix View source, --portable/ -P on Windows
Granite (Beta): about dialog, static notebook, no "New Tab" in toolbar, Print → Share
Support building with Wayland-enabled GTK+3
Theming: content view, secondary toolbar class, drop old icon names, bigger error icon
Introduce --plain mode equivalent to GtkLauncher, lazy URLs for --snapshot/ -s
Log bookmarks, history and downloads to zeitgeist
Show security details and export certificates with GCR, error out instead of colored urlbar
Only allow data: URLs in urlbar for images
Recognize and cache HSTS, system-wide /etc/xdg/midori/hsts
Strip HTTP Host to outsmart some filter proxies
Completion: Fix PageUp/Down, Shift+Tab and wrap: This is consistent with GTK+ (excluding Tab) and Firefox
Change Focus Current Tab to Ctrl+Alt+Home
Fix Shift+Space for scrolling upwards
Control+Alt+R: Readable mode
Handle access key in link hints
Drop speed dial keyboard access in favour of "." link hints
No Open, Bookmark bar, Customize toolbar, Inspect page in app menu; split panel menu
Use ellipsises instead of period thresomes
Hinted text in bookmarks, history and cookie manager
Ellipsize panels (except for Transfers)
Add icon to bookmark dialog and remove labels
Validate proxy server IP and render invalid URLs in GTK+3
Rename "Toplevel" folder to "Bookmarks"
Chrome identification option; "Automatic" user agent is Chrome-based
Search: Create engines from search forms, remove "icon" field
Copy Image s/Address// always copy both URL and data
Rework debugging by introducing MIDORI_DEBUG; about:paths
Adblock: Refresh filters based on file time and meta data, abp: links
Optionally save website including resources
Merged NextForward akin to StopReload
PanedAction, Viewable, SpeedDial, (most of) Settings, Paths in Vala
Improved database: requires sqlite 3.6.19 and 0.2.6 in import dialog
Confirm Caret Browsing before enabling it
Support for custom items in Statusbar Features (see FAQ)
Draggable favicon as URL or text, URL icon for URL entries
Remember if inspector was attached
Open tabs in the background by default
RTL support in special/ error pages
Fix progressbar text with GTK+3
Build fix: More robust GTK+2 version check
Ensure progress in urlbar and tab match
Zoom text and images by default
Don't mixup tokens starting with the same letters
Seemless running out of build folder
No speed dial in --app/ --private, fix layout with many tiles
Add X-GNOME-Fullname to .desktop and translate desktop shortcuts
Delayed Load extension
v0.4.6:
+ Fix crasher in geolocation infobar
+ Fix crasher in about:version on some systems
+ Fix crasher opening bookmarks from Unity global menu
+ Use WebKitFaviconDatabase as of WebKit 1.8.0
+ Use midori-prefixed temp folder in midori_view_save_source
+ Fix cancelling downloads with SteadyFlow or Aria2
+ Fix crash dialog instead of opening tab in a running window
+ Fix page icons in multi-frame sites (gmail, tumbler)
+ Distinguish Simplified and Traditional Chinese
+ Support go-jump-symbolic
+ Handle empty tabs due to download links with a target
+ Handle frame load interrupted in the unholy trinity
+ Fix libsoup version check and wrong SSL status in location
v0.4.5:
+ Work around black border around widgets on Win32
+ Whitelist direct/ re-directed navigation requests in adblock
+ Require Vala 0.14
+ Provide geolocation diagnostics in about:geolocation
+ List available about: URLs and app instance name in about:version
+ Replace illegal characters in download filenames
+ Tweak app options on Win32 and use ShellExecuteEx in sokoke_show_uri
+ Use sokoke_show_uri in midori_browser_download_status_cb
+ External Download manager Steadyflow and Aria2 (with cookies)
+ Ensure adblock config folder when blocking images
+ Use sqlite WAL mode for history if available
+ Allow relative -c/ --config path
+ Context menus on Back and Forward toolbar items
+ Always show the tabbar by default
+ Use ubuntu-bug if it exists
+ Show inline find while typing and statusbar text in overlay with GTK+ 3.2
+ Esc/ closing "downloads still active" should cancel, not continue
+ Optional Granite support for notebook and bookmark dialog as pop-over
+ Ctrl+j to toggle statusbar aka downloads
+ Show at most 3 search engines in completion
+ Don't replace existing onclick/ blur with autosuggest
+ Implement low_memory_profile for FreeBSD and Win32
+ Use var in internal javascript, to fix Google apps
+ Handle download requests in frames
v0.4.4:
+ Disable page cache with < 352 MB RAM
+ Display filename in download dialog
+ Fix box packing in GTK+3 (in most cases)
+ Enable experimental HTML5 fullscreen API
+ Harden IPv6 address recognition in location
+ Experimental site data policy support (see FAQ)
+ Close tabs by middle clicking close button
+ Merge cookies and other data in Clear Private Data
+ Improve KatzeArrayAction for Unity menuproxy compatibility
+ Use GDateTime for history to avoid broken C runtimes
+ Add Midori tag to DuckDuckGo default URI
+ Rewrite completion popup resizing
+ Streamline page icon loading stages and fallbacks
+ Disable clipboard work-around for WebKit >= 1.4.3
+ Re-word .desktop entry as an action
+ Display informative text in private browsing
+ Consistent clear icons in entries
+ Revised download filename generation
+ Add 'Open in Image Viewer' menu item
+ Formhistory 2.0 with GDOM support
+ Handle javascript: and mailto: links better
+ Handle = key in Ukrainian layout better
+ Fix bookmark export and deletion of bookmark folders
+ Speed dial shortcut re-reordering by DND
v0.4.3: v0.4.3:
+ Implement about:widgets to test rendering + Implement about:widgets to test rendering
+ Fix resizing of inspector by applying a minimum size + Fix resizing of inspector by applying a minimum size

38
INSTALL
View file

@ -14,10 +14,10 @@ Run './waf build'
You can now run Midori from the build folder like so You can now run Midori from the build folder like so
'./waf build --run' '_build/default/midori/midori'
Using --run as shown above will make sure extensions as well as Midori will pick up extensions and resources from the build folder;
localizations are used from the build folder. it will NOT use localizations.
You can install it with './waf install' You can install it with './waf install'
@ -29,15 +29,16 @@ For further options run './waf --help'
+++ Debugging Midori +++ +++ Debugging Midori +++
Run './waf configure -d full' from the Midori folder. Midori is by default built with debugging symbols, make sure you have
installed 'gdb', the GNU Debugger.
Run './waf build' It's a good idea to execute all unit test cases and see that they pass.
Midori is now built with debugging symbols. 'xvfb-run ./waf check'
Make sure you have installed 'gdb', the GNU Debugger. In this example, Xvfb is used to avoid relying on the local user setup.
Run Midori as 'gdb _build_/default/midori/midori'. You can also run Midori proper as 'gdb _build/default/midori/midori'.
Inside gdb, type 'run'. Inside gdb, type 'run'.
@ -49,27 +50,32 @@ function names and line numbers.
If the problem is a warning and not a crash, try this: If the problem is a warning and not a crash, try this:
'G_DEBUG=all gdb _build_/default/midori/midori' 'G_DEBUG=all gdb _build/default/midori/midori'
If you are interested in HTTP communication, try this: If you are interested in HTTP communication, try this:
'MIDORI_SOUP_DEBUG=2 _build_/default/midori/midori' 'MIDORI_DEBUG=headers _build/default/midori/midori'
Where '2' can be a level between 0 and 3. Where 'headers' can be replaced with 'body' to get full message contents.
If you are interested in (non-) touchscreen behaviour, try this: If you are interested in (non-) touchscreen behaviour, try this:
'MIDORI_TOUCHSCREEN=1 _build_/default/midori/midori', or 'MIDORI_TOUCHSCREEN=1 _build/default/midori/midori', or
'MIDORI_TOUCHSCREEN=0 _build_/default/midori/midori' 'MIDORI_TOUCHSCREEN=0 _build/default/midori/midori'
If you want to "dry run" without WebKitGTK+ rendering, try this: If you want to "dry run" without WebKitGTK+ rendering, try this:
'MIDORI_UNARMED=1 _build_/default/midori/midori' 'MIDORI_DEBUG=unarmed _build/default/midori/midori'
To debug extensions you can specify the path: If you want to test bookmarks, you can enable database tracing:
'export MIDORI_EXTENSION_PATH=_build_/default/extensions' 'MIDORI_DEBUG=bookmarks _build/default/midori/midori'
To disable Netscape plugins, use MOZ_PLUGIN_PATH=/.
When running from the build folder, extensions will also be located
in the build folder (setting MIDORI_EXTENSION_PATH is no longer needed).
For further information a tutorial for gdb and For further information a tutorial for gdb and
reading up on how you can install debugging reading up on how you can install debugging

16
README
View file

@ -2,19 +2,19 @@ This file is licensed under the terms of the expat license, see the file EXPAT.
Midori is a lightweight web browser. Midori is a lightweight web browser.
* Full integration with GTK+2. * Full integration with GTK+2 and GTK+3.
* Fast rendering with WebKit. * Fast rendering with WebKit and HTML5 video with GStreamer.
* Tabs, windows and session management. * Tabs, windows and session management.
* Flexibly configurable Web Search. * History completion and configurable Web Search.
* User scripts and user styles support. * User scripts and user styles support.
* Adblock Plus compatible, external download manager support.
* Straightforward bookmark management. * Straightforward bookmark management.
* Customizable and extensible interface. * Customizable interface, extensions written in C and Vala.
* Extensions written in C.
Requirements: GLib 2.22, GTK+ 2.10, WebkitGTK+ 1.1.17, libXML2, Requirements: GLib 2.22, GTK+ 2.16, WebkitGTK+ 1.1.17, libXML2,
libsoup 2.27.90, sqlite 3.0, Vala 0.10 libsoup 2.27.90, sqlite 3.0, Vala 0.14
Optional: GTK+ 3.0, Unique 0.9, libnotify Optional: GTK+ 3.0, Unique 0.9, libnotify, gcr, Granite 0.2, WebKit2GTK+ 1.11.91/ 2.0.0
For installation instructions read INSTALL. For installation instructions read INSTALL.

2
configure vendored
View file

@ -130,7 +130,7 @@ clean:
distclean: distclean:
@$WAF distclean @$WAF distclean
@-rm -rf _build_ @-rm -rf _build
@-rm -f Makefile @-rm -f Makefile
check: check:

71
data/about.css Normal file
View file

@ -0,0 +1,71 @@
/*
about: page style template for Midori.
This file is licensed under the terms of the expat license, see the file EXPAT.
*/
body {
background-color: #eee;
margin: 0;
padding: 0;
}
#container {
background: #f6fff3;
min-width: 70%;
max-width: 70%;
margin: 2em auto 1em;
padding: 1em;
border: 0.2em solid #9acb7f;
-webkit-border-radius: 1em;
}
#icon {
float: left;
padding-left: 1%;
padding-top: 1%;
}
html[dir="rtl"] #icon {
float: right;
padding-right: 1%;
}
#main {
float: right;
width: 75%;
}
h1 {
font-size: 1.4em;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#logo {
position: absolute; bottom: 15px;
z-index: -1;
}
html[dir="ltr"] #logo {
right: 15px;
}
html[dir="rtl"] #logo {
left: 15px;
}
button span,
button img {
vertical-align: middle;
padding: 2px 1px;
}
#message {
font-size: 1.1em;
word-wrap: break-word;
}
#description {
font-size: 1em;
}

View file

@ -186,10 +186,10 @@ AutoSuggestControl.prototype.init = function () {
}; };
//assign onblur event handler (hides suggestions) //assign onblur event handler (hides suggestions)
this.textbox.onblur = if (!this.textbox.onblur)
this.textbox.onclick = function () { this.textbox.onblur = function () { oThis.hideSuggestions(); };
oThis.hideSuggestions(); if (!this.textbox.onclick)
}; this.textbox.onclick = function () { oThis.hideSuggestions(); };
//create the suggestions dropdown //create the suggestions dropdown
this.createDropDown(); this.createDropDown();
@ -299,7 +299,7 @@ function initSuggestions () {
if (inputs.length == 0) if (inputs.length == 0)
return false; return false;
for (i=0;i<inputs.length;i++) for (var i=0;i<inputs.length;i++)
{ {
var ename = inputs[i].getAttribute("name"); var ename = inputs[i].getAttribute("name");
var eid = inputs[i].getAttribute("id"); var eid = inputs[i].getAttribute("id");

View file

@ -2,67 +2,11 @@
Error page template for Midori. Error page template for Midori.
This file is licensed under the terms of the expat license, see the file EXPAT. This file is licensed under the terms of the expat license, see the file EXPAT.
--> -->
<html dir="{dir}">
<html>
<head> <head>
<title>{title}</title> <title>{title}</title>
<link rel="shortcut icon" href="{icon}" /> {favicon}
<style type="text/css"> <link rel="stylesheet" type="text/css" href="res://about.css" />
body {
background-color: #eee;
margin: 0;
padding: 0;
}
#container {
background: #f6fff3;
min-width: 70%;
max-width: 70%;
margin: 2em auto 1em;
padding: 1em;
border: 0.2em solid #9acb7f;
-webkit-border-radius: 1em;
}
icon {
float: left;
padding-left: 1%;
padding-top: 1%;
}
#main {
float: right;
width: 90%;
}
h1 {
font-size: 1.4em;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#logo {
position: absolute; right: 15px; bottom: 15px;
z-index: -1;
}
button span,
button img {
vertical-align: middle;
padding: 2px 1px;
}
message {
font-size: 1.1em;
}
description {
font-size: 1em;
}
</style>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
@ -74,7 +18,7 @@ description {
<p id="description">{description}</p> <p id="description">{description}</p>
<form method="GET" action="{uri}"> <form method="GET" action="{uri}">
<button type="submit" onclick="location.reload(); return false;"> <button type="submit" onclick="location.reload(); return false;">
<img src="stock://gtk-refresh"/> <img style="{hide-button-images}" src="stock://gtk-refresh"/>
<span>{tryagain}</span> <span>{tryagain}</span>
</button> </button>
</form> </form>

View file

@ -1,2 +1,2 @@
[settings] [settings]
filters=http://adblockplus.mozdev.org/easylist/easylist.txt;https://easylist-downloads.adblockplus.org/easyprivacy.txt filters=https://easylist-downloads.adblockplus.org/easylist.txt;https://easylist-downloads.adblockplus.org/easyprivacy.txt

View file

@ -8,29 +8,64 @@ Stylesheet for Midori's documentation based on a version of Enrico Troeger.
@media screen { @media screen {
body { html, body {
background-color: #f6fff3; width: 100% !important;
color: #404040; height: 100% !important;
margin-left: 0.4em; margin: 0 !important;
width: 60em; padding: 0 !important;
font-size: 90%;
} }
a { * {
color: #013100; background: #f6fff3 !important;
color: #404040 !important;
font-size: 14pt !important;
font-family: serif !important;
text-align: justify !important;
line-height: 1.4em !important;
word-spacing: 0.4mm !important;
letter-spacing: 0.2mm !important;
-webkit-column-count: auto !important;
-webkit-column-width: auto !important;
-webkit-box-shadow: none !important;
width: auto !important;
word-wrap: break-word !important;
} }
a:visited { div, p {
color: #7E558E; padding: 5pt !important;
}
li {
padding-left: 5pt !important;
}
img, *[accesskey], form *, form, iframe,
*[id^=navigation], *[id$=navigation], *[id*=navigation], .collapsed, .expanded {
display: none !important
}
/* FIXME: we want "images bigger than 50px here" */
img[width] {
display: inline !important
}
:link, :link * {
color: #013100 !important;
text-decoration: underline !important;
}
:visited, :visited * {
color: #7E558E !important;
text-decoration: underline !important;
} }
a:hover { a:hover {
text-decoration: none; text-decoration: none !important;
} }
h1, h2, h3 { h1, h2, h3 {
font-family: sans-serif; font-family: serif !important;
color: #002a00; color: #002a00 !important;
} }
h1 { h1 {

File diff suppressed because it is too large Load diff

28
data/gtk3.css Normal file
View file

@ -0,0 +1,28 @@
.notebook tab .button {
-GtkButton-default-border: 0;
-GtkButton-default-outside-border: 0;
-GtkButton-inner-border: 0;
-GtkWidget-focus-line-width: 0;
-GtkWidget-focus-padding: 0;
padding: 0;
}
GtkOverlay > * {
padding: 4px;
border-style: solid;
border-radius: 0 5px 0 0;
border-width: 1px 1px 0 0;
}
GtkOverlay MidoriFindbar {
border-radius: 0 0 0 5px;
border-width: 0 0 1px 1px; /* top right bottom left */
}
/* Kill grey backround on inactive buttons */
GtkDrawingArea,
GtkImage,
GtkImage:insensitive,
GtkImage:selected {
background-color: @transparent;
}

View file

@ -9,6 +9,7 @@ Exec=midori --private %U
Icon=midori Icon=midori
Terminal=false Terminal=false
StartupNotify=true StartupNotify=true
NotShowIn=Pantheon;
X-Osso-Type=application/x-executable X-Osso-Type=application/x-executable
X-Osso-Service=midori X-Osso-Service=midori

View file

@ -3,7 +3,8 @@ Version=1.0
Type=Application Type=Application
_Name=Midori _Name=Midori
_GenericName=Web Browser _GenericName=Web Browser
_Comment=Lightweight web browser _X-GNOME-Fullname=Midori Web Browser
_Comment=Browse the Web
_X-GNOME-Keywords=Internet;WWW;Explorer _X-GNOME-Keywords=Internet;WWW;Explorer
_X-AppInstall-Keywords=Internet;WWW;Explorer _X-AppInstall-Keywords=Internet;WWW;Explorer
Categories=GTK;Network;WebBrowser; Categories=GTK;Network;WebBrowser;
@ -14,20 +15,17 @@ Terminal=false
StartupNotify=true StartupNotify=true
X-Osso-Type=application/x-executable X-Osso-Type=application/x-executable
X-Osso-Service=midori X-Osso-Service=midori
X-Ayatana-Desktop-Shortcuts=TabNew;WindowNew;Private Actions=TabNew;WindowNew;Private;
[TabNew Shortcut Group] [Desktop Action TabNew]
Name=New _Tab _Name=New Tab
Exec=midori -e TabNew Exec=midori -e TabNew
TargetEnvironment=Unity
[WindowNew Shortcut Group] [Desktop Action WindowNew]
Name=New _Window _Name=New Window
Exec=midori -e WindowNew Exec=midori -e WindowNew
TargetEnvironment=Unity
[Private Shortcut Group] [Desktop Action Private]
Name=New P_rivate Browsing Window _Name=New Private Browsing Window
Exec=midori --private Exec=midori --private
TargetEnvironment=Unity

BIN
data/midori.swf Normal file

Binary file not shown.

View file

@ -27,3 +27,10 @@ name=The Free Dictionary
text=Dictionary, Encyclopedia and Thesaurus text=Dictionary, Encyclopedia and Thesaurus
uri=http://www.thefreedictionary.com/%s uri=http://www.thefreedictionary.com/%s
token=fd token=fd
[Google Translate]
name=Google Translate
text=Localize text or URL
uri=http://translate.google.com/?q=
token=gt

View file

@ -55,10 +55,7 @@
width: 85%; width: 85%;
height: 75%; height: 75%;
margin: auto; margin: auto;
-webkit-box-shadow: 0 4px 18px rgba(0,0,0,.3), 0 0 2px #fff inset; -webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
background-image: -webkit-gradient(
linear, center top, center bottom,
from(#f6f6f6), to(#e3e3e3));
border: 1px solid #bcbcbc; border: 1px solid #bcbcbc;
border-bottom-color: #a0a0a0; border-bottom-color: #a0a0a0;
position: relative; position: relative;
@ -75,9 +72,15 @@
div.shortcut .preview.new .add { div.shortcut .preview.new .add {
display: block; display: block;
height: 100%; height: 100%;
width: 50%; width: 100%;
margin: 0 auto; margin: 0 auto;
cursor: pointer; cursor: pointer;
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
background-image: -webkit-gradient(
linear, center top, center bottom,
from(#f6f6f6), to(#e3e3e3));
background-repeat: repeat-x;
-webkit-border-radius: 3px;
} }
.title { .title {
@ -111,95 +114,148 @@
display:none; display:none;
} }
div.osd { .selected {
top: 9px; outline: 1px dotted black;
position: fixed; background-color: #eef;
width: 100%;
text-align: right;
}
div.osd span {
border: 1px solid #999;
background-color: #f5f5f5;
padding: 8px;
color: #999;
-webkit-border-bottom-left-radius: 10px;
visibility: hidden;
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
var getAction = function (id) function add_tile (ev) {
{ ev.preventDefault();
var s = document.getElementById(id).childNodes[0];
if (s.className == 'preview')
return true;
var url = prompt ("{enter_shortcut_address}", "http://"); var url = prompt ("{enter_shortcut_address}", "http://");
if (!url) return false; if (!url)
return false;
if (url.indexOf ("://") == -1) if (url.indexOf ("://") == -1)
url = "http://" + url; url = "http://" + url;
console.log ("speed_dial-save-add " + id + " " + url + " "); var id = ev.target.parentNode.parentNode.id;
return false; console.log ("speed_dial-save-add " + id + " " + url);
} }
var renameShortcut = function (id) function rename_tile (ev) {
{ var old_name = ev.target.textContent;
var old_name = document.getElementById(id).childNodes[1].textContent;
var name = prompt ("{enter_shortcut_name}", old_name); var name = prompt ("{enter_shortcut_name}", old_name);
if (!name) return; if (!name)
return;
var id = ev.target.parentNode.id;
console.log ("speed_dial-save-rename " + id + " " + name); console.log ("speed_dial-save-rename " + id + " " + name);
} }
var clearShortcut = function (id) function delete_tile (ev) {
{ ev.preventDefault();
if (!confirm("{are_you_sure}")) if (!confirm("{are_you_sure}"))
return; return;
var id = ev.target.parentNode.parentNode.id;
console.log ("speed_dial-save-delete " + id); console.log ("speed_dial-save-delete " + id);
} }
var key_id = 's';
var key_timeout;
document.onkeypress = function () var firstNode, secondNode;
{ var cursor;
key_id = key_id + String.fromCharCode (event.which);
clearTimeout (key_timeout); var get_dial_div = function (ele) {
var dial_div;
document.getElementById('dialing').innerText = key_id.substr(1); if (ele.nodeName == 'IMG')
document.getElementById('dialing').style.visibility = 'visible'; dial_div = ele.parentNode.parentNode.parentNode;
if (ele.className == 'title')
var div = document.getElementById(key_id); dial_div = ele.parentNode;
if (div) if (ele.className.indexOf ('shortcut') != -1)
{ dial_div = ele;
if (key_id.substr(1) > 9) return dial_div;
{
if (getAction (key_id))
document.location = div.childNodes[0].childNodes[1].href;
key_id = 's';
}
else
key_timeout = setTimeout ('if (getAction (key_id)) document.location = document.getElementById(key_id).childNodes[0].childNodes[1].href; key_id = \'s\'', 1000);
}
else
key_id = 's';
if (key_id.length <= 1)
document.getElementById('dialing').style.visibility = 'hidden';
return false;
} }
function click (ev) {
if (ev == undefined)
return;
ev.preventDefault();
var ele = ev.target;
cursor = ele.style.cursor;
ele.style.cursor = 'move';
var eparent = get_dial_div (ele);
if (eparent != undefined) {
eparent.className = 'shortcut selected';
firstNode = eparent.id;
}
};
function up (ev) {
if (ev == undefined)
return;
ev.preventDefault();
ele = ev.target;
var eparent = get_dial_div (ele);
ele.style.cursor = cursor;
secondNode = eparent.id;
/* ommit just mere clicking the dial */
if (firstNode != secondNode && firstNode != undefined)
swap();
};
function over (ev) {
if (ev == undefined)
return;
ev.preventDefault();
var ele = ev.target;
var eparent = get_dial_div (ele);
var dial = document.getElementsByClassName("shortcut");
if (firstNode != undefined)
{
eparent.className = 'shortcut selected';
for (var i = 0; i < dial.length; i++) {
if (eparent.id != firstNode.id && dial[i].id != eparent.id) {
dial[i].className = 'shortcut';
}
}
}
ele.style.cursor = cursor;
}
function swap () {
console.log ("speed_dial-save-swap " + firstNode + " " + secondNode);
};
function init () {
var new_tile = document.getElementsByClassName ("preview new");
new_tile[0].addEventListener ('click', add_tile, false);
var titles = document.getElementsByClassName ("title");
var len = titles.length;
for (var i = 0; i < len; i++) {
if (titles[i].parentNode.childNodes[0].className != "preview new")
titles[i].addEventListener ('click', rename_tile, false);
}
var crosses = document.getElementsByClassName ("cross");
var len = crosses.length;
for (var i = 0; i < len; i++)
crosses[i].addEventListener ('click', delete_tile, false);
var occupied_tiles = document.getElementsByClassName ("shortcut");
var len = occupied_tiles.length;
for (var i = 0; i < len; i++) {
if (occupied_tiles[i].childNodes[0].className != "preview new") {
occupied_tiles[i].addEventListener('mousedown', click, false);
occupied_tiles[i].addEventListener('mouseover', over, false);
occupied_tiles[i].addEventListener('mouseup', up, false);
}
}
}
</script> </script>
</head> </head>
<body> <body onload="init ();">
<div class="osd" >
<span id="dialing"></span>
</div>
<div id="content"> <div id="content">

View file

@ -6,18 +6,20 @@ import pproc as subprocess
import os import os
import Utils import Utils
blddir = '_build' # recognized by ack
for module in ('midori', 'katze'): for module in ('midori', 'katze'):
try: try:
if not os.access ('_build_', os.F_OK): if not os.access (blddir, os.F_OK):
Utils.check_dir ('_build_') Utils.check_dir (blddir)
if not os.access ('_build_/docs', os.F_OK): if not os.access (blddir + '/docs', os.F_OK):
Utils.check_dir ('_build_/docs') Utils.check_dir (blddir + '/docs')
if not os.access ('_build_/docs/api', os.F_OK): if not os.access (blddir + '/docs/api', os.F_OK):
Utils.check_dir ('_build_/docs/api') Utils.check_dir (blddir + '/docs/api')
subprocess.call (['gtkdoc-scan', '--module=' + module, subprocess.call (['gtkdoc-scan', '--module=' + module,
'--source-dir=' + module, '--output-dir=_build_/docs/api/' + module, '--source-dir=' + module, '--output-dir=' + blddir + '/docs/api/' + module,
'--rebuild-sections', '--rebuild-types']) '--rebuild-sections', '--rebuild-types'])
os.chdir ('_build_/docs/api/' + module) os.chdir (blddir + '/docs/api/' + module)
subprocess.call (['gtkdoc-mktmpl', '--module=' + module, subprocess.call (['gtkdoc-mktmpl', '--module=' + module,
'--output-dir=.' + module]) '--output-dir=.' + module])
subprocess.call (['gtkdoc-mkdb', '--module=' + module, subprocess.call (['gtkdoc-mkdb', '--module=' + module,

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
/* This extensions add support for user addons: userscripts and userstyles */ /* This extensions add support for user addons: userscripts and userstyles */
#include <midori/midori.h> #include <midori/midori.h>
#include "midori-core.h"
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include "config.h" #include "config.h"
@ -182,8 +183,8 @@ addons_install_response (GtkWidget* infobar,
if (!filename) if (!filename)
filename = g_path_get_basename (uri); filename = g_path_get_basename (uri);
folder_path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), folder_path = g_build_path (G_DIR_SEPARATOR_S,
PACKAGE_NAME, folder, NULL); midori_paths_get_user_data_dir (), PACKAGE_NAME, folder, NULL);
if (!g_file_test (folder_path, G_FILE_TEST_EXISTS)) if (!g_file_test (folder_path, G_FILE_TEST_EXISTS))
katze_mkdir_with_parents (folder_path, 0700); katze_mkdir_with_parents (folder_path, 0700);
@ -242,9 +243,8 @@ addons_notify_load_status_cb (MidoriView* view,
MidoriExtension* extension) MidoriExtension* extension)
{ {
const gchar* uri = midori_view_get_display_uri (view); const gchar* uri = midori_view_get_display_uri (view);
WebKitWebView* web_view = WEBKIT_WEB_VIEW (midori_view_get_web_view (view));
if (webkit_web_view_get_view_source_mode (web_view)) if (midori_tab_get_view_source (MIDORI_TAB (view)))
return; return;
if (uri && *uri) if (uri && *uri)
@ -293,13 +293,13 @@ addons_button_add_clicked_cb (GtkToolItem* toolitem,
if (addons->kind == ADDONS_USER_SCRIPTS) if (addons->kind == ADDONS_USER_SCRIPTS)
{ {
addons_type = g_strdup ("userscripts"); addons_type = g_strdup ("userscripts");
path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), path = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_user_data_dir (),
PACKAGE_NAME, "scripts", NULL); PACKAGE_NAME, "scripts", NULL);
} }
else if (addons->kind == ADDONS_USER_STYLES) else if (addons->kind == ADDONS_USER_STYLES)
{ {
addons_type = g_strdup ("userstyles"); addons_type = g_strdup ("userstyles");
path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), path = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_user_data_dir (),
PACKAGE_NAME, "styles", NULL); PACKAGE_NAME, "styles", NULL);
} }
else else
@ -336,23 +336,13 @@ addons_button_add_clicked_cb (GtkToolItem* toolitem,
if (!g_file_test (path, G_FILE_TEST_EXISTS)) if (!g_file_test (path, G_FILE_TEST_EXISTS))
katze_mkdir_with_parents (path, 0700); katze_mkdir_with_parents (path, 0700);
#if !GTK_CHECK_VERSION (2, 14, 0)
files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog));
#else
files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog)); files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog));
#endif
while (files) while (files)
{ {
GFile* src_file; GFile* src_file;
GError* error = NULL; GError* error = NULL;
#if !GTK_CHECK_VERSION (2, 14, 0)
src_file = g_file_new_for_path (files);
#else
src_file = files->data; src_file = files->data;
#endif
if (G_IS_FILE (src_file)) if (G_IS_FILE (src_file))
{ {
GFile* dest_file; GFile* dest_file;
@ -491,7 +481,7 @@ addons_open_in_editor_clicked_cb (GtkWidget* toolitem,
g_object_get (settings, "text-editor", &text_editor, NULL); g_object_get (settings, "text-editor", &text_editor, NULL);
if (text_editor && *text_editor) if (text_editor && *text_editor)
sokoke_spawn_program (text_editor, element->fullpath); sokoke_spawn_program (text_editor, TRUE, element->fullpath, TRUE, FALSE);
else else
{ {
gchar* element_uri = g_filename_to_uri (element->fullpath, NULL, NULL); gchar* element_uri = g_filename_to_uri (element->fullpath, NULL, NULL);
@ -522,10 +512,13 @@ addons_open_target_folder_clicked_cb (GtkWidget* toolitem,
folder = g_path_get_dirname (element->fullpath); folder = g_path_get_dirname (element->fullpath);
} }
else else
folder = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), {
PACKAGE_NAME, folder = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_user_data_dir (),
addons->kind == ADDONS_USER_SCRIPTS PACKAGE_NAME, addons->kind == ADDONS_USER_SCRIPTS
? "scripts" : "styles", NULL); ? "scripts" : "styles", NULL);
katze_mkdir_with_parents (folder, 0700);
}
folder_uri = g_filename_to_uri (folder, NULL, NULL); folder_uri = g_filename_to_uri (folder, NULL, NULL);
g_free (folder); g_free (folder);
@ -783,11 +776,10 @@ addons_treeview_render_text_cb (GtkTreeViewColumn* column,
gtk_tree_model_get (model, iter, 0, &element, -1); gtk_tree_model_get (model, iter, 0, &element, -1);
g_object_set (renderer, "text", element->displayname, NULL); g_object_set (renderer, "text", element->displayname,
if (!element->enabled) "sensitive", element->enabled,
g_object_set (renderer, "sensitive", false, NULL); "ellipsize", PANGO_ELLIPSIZE_END,
else NULL);
g_object_set (renderer, "sensitive", true, NULL);
} }
static void static void
@ -831,19 +823,16 @@ addons_get_directories (AddonsKind kind)
else else
g_assert_not_reached (); g_assert_not_reached ();
path = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), path = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_user_data_dir (),
PACKAGE_NAME, folder_name, NULL); PACKAGE_NAME, folder_name, NULL);
if (g_access (path, X_OK) == 0)
directories = g_slist_prepend (directories, path); directories = g_slist_prepend (directories, path);
else
g_free (path);
datadirs = g_get_system_data_dirs (); datadirs = g_get_system_data_dirs ();
while (*datadirs) while (*datadirs)
{ {
path = g_build_path (G_DIR_SEPARATOR_S, *datadirs, path = g_build_path (G_DIR_SEPARATOR_S, *datadirs,
PACKAGE_NAME, folder_name, NULL); PACKAGE_NAME, folder_name, NULL);
if (g_slist_find (directories, path) == NULL && g_access (path, X_OK) == 0) if (g_slist_find (directories, path) == NULL)
directories = g_slist_prepend (directories, path); directories = g_slist_prepend (directories, path);
else else
g_free (path); g_free (path);
@ -940,10 +929,16 @@ js_metadata_from_file (const gchar* filename,
/* We don't support these, so abort here */ /* We don't support these, so abort here */
g_free (line); g_free (line);
g_io_channel_shutdown (channel, false, 0); g_io_channel_shutdown (channel, false, 0);
if (includes != NULL)
{
g_slist_free (*includes); g_slist_free (*includes);
g_slist_free (*excludes);
*includes = NULL; *includes = NULL;
}
if (excludes != NULL)
{
g_slist_free (*excludes);
*excludes = NULL; *excludes = NULL;
}
return FALSE; return FALSE;
} }
else if (includes && g_str_has_prefix (line, "// @include")) else if (includes && g_str_has_prefix (line, "// @include"))
@ -1335,8 +1330,9 @@ addons_init (Addons* addons)
G_CALLBACK (addons_cell_renderer_toggled_cb), addons); G_CALLBACK (addons_cell_renderer_toggled_cb), addons);
gtk_tree_view_append_column (GTK_TREE_VIEW (addons->treeview), column); gtk_tree_view_append_column (GTK_TREE_VIEW (addons->treeview), column);
column = gtk_tree_view_column_new (); column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_expand (column, TRUE);
renderer_text = gtk_cell_renderer_text_new (); renderer_text = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer_text, FALSE); gtk_tree_view_column_pack_start (column, renderer_text, TRUE);
gtk_tree_view_column_set_cell_data_func (column, renderer_text, gtk_tree_view_column_set_cell_data_func (column, renderer_text,
(GtkTreeCellDataFunc)addons_treeview_render_text_cb, (GtkTreeCellDataFunc)addons_treeview_render_text_cb,
addons->treeview, NULL); addons->treeview, NULL);
@ -1526,30 +1522,19 @@ addons_add_tab_cb (MidoriBrowser* browser,
G_CALLBACK (addons_notify_load_status_cb), extension); G_CALLBACK (addons_notify_load_status_cb), extension);
} }
static void
addons_add_tab_foreach_cb (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
addons_add_tab_cb (browser, view, extension);
}
static void
addons_deactivate_tabs (MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_handlers_disconnect_by_func (
web_view, addons_context_ready_cb, extension);
}
static void static void
addons_browser_destroy (MidoriBrowser* browser, addons_browser_destroy (MidoriBrowser* browser,
MidoriExtension* extension) MidoriExtension* extension)
{ {
GtkWidget* scripts, *styles; GtkWidget* scripts, *styles;
GList* tabs = midori_browser_get_tabs (browser);
midori_browser_foreach (browser, (GtkCallback)addons_deactivate_tabs, extension); for (; tabs; tabs = g_list_next (tabs))
{
GtkWidget* web_view = midori_view_get_web_view (tabs->data);
g_signal_handlers_disconnect_by_func (
web_view, addons_context_ready_cb, extension);
}
g_list_free (tabs);
g_signal_handlers_disconnect_by_func (browser, addons_add_tab_cb, extension); g_signal_handlers_disconnect_by_func (browser, addons_add_tab_cb, extension);
scripts = (GtkWidget*)g_object_get_data (G_OBJECT (browser), "scripts-addons"); scripts = (GtkWidget*)g_object_get_data (G_OBJECT (browser), "scripts-addons");
@ -1629,9 +1614,10 @@ addons_app_add_browser_cb (MidoriApp* app,
{ {
GtkWidget* panel; GtkWidget* panel;
GtkWidget* scripts, *styles; GtkWidget* scripts, *styles;
GList* tabs = midori_browser_get_tabs (browser);
midori_browser_foreach (browser, for (; tabs; tabs = g_list_next (tabs))
(GtkCallback)addons_add_tab_foreach_cb, extension); addons_add_tab_cb (browser, tabs->data, extension);
g_list_free (tabs);
g_signal_connect (browser, "add-tab", g_signal_connect (browser, "add-tab",
G_CALLBACK (addons_add_tab_cb), extension); G_CALLBACK (addons_add_tab_cb), extension);
panel = katze_object_get_object (browser, "panel"); panel = katze_object_get_object (browser, "panel");
@ -1687,10 +1673,10 @@ addons_save_settings (MidoriApp* app,
config_dir = midori_extension_get_config_dir (extension); config_dir = midori_extension_get_config_dir (extension);
config_file = g_build_filename (config_dir, "addons", NULL); config_file = g_build_filename (config_dir, "addons", NULL);
if (config_dir != NULL)
katze_mkdir_with_parents (config_dir, 0700); katze_mkdir_with_parents (config_dir, 0700);
sokoke_key_file_save_to_file (keyfile, config_file, &error); sokoke_key_file_save_to_file (keyfile, config_file, &error);
/* If the folder is /, this is a test run, thus no error */ if (error && midori_extension_get_config_dir (extension) != NULL)
if (error && !g_str_equal (config_dir, "/"))
{ {
g_warning (_("The configuration of the extension '%s' couldn't be saved: %s\n"), g_warning (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
_("User addons"), error->message); _("User addons"), error->message);
@ -1866,7 +1852,6 @@ addons_activate_cb (MidoriExtension* extension,
G_CALLBACK (addons_deactivate_cb), app); G_CALLBACK (addons_deactivate_cb), app);
} }
#ifdef G_ENABLE_DEBUG
static void static void
test_addons_simple_regexp (void) test_addons_simple_regexp (void)
{ {
@ -1905,7 +1890,6 @@ extension_test (void)
{ {
g_test_add_func ("/extensions/addons/simple_regexp", test_addons_simple_regexp); g_test_add_func ("/extensions/addons/simple_regexp", test_addons_simple_regexp);
} }
#endif
MidoriExtension* MidoriExtension*
extension_init (void) extension_init (void)

288
extensions/apps.vala Normal file
View file

@ -0,0 +1,288 @@
/*
Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
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 Apps {
const string EXEC_PREFIX = PACKAGE_NAME + " -a ";
private class Launcher : GLib.Object, GLib.Initable {
internal GLib.File file;
internal string name;
internal string icon_name;
internal string exec;
internal string uri;
internal static async void create (GLib.File folder, string uri, string title) {
/* Strip LRE leading character and / */
string filename = title.delimit ("/", ' ') + ".desktop";
string exec = EXEC_PREFIX + uri;
string name = title;
// TODO: Midori.Paths.get_icon save to png
string icon_name = Midori.Stock.WEB_BROWSER;
string contents = """
[Desktop Entry]
Version=1.0
Type=Application
Name=%s
Exec=%s
TryExec=%s
Icon=%s
Categories=Network;
""".printf (name, exec, PACKAGE_NAME, icon_name);
var file = folder.get_child (filename);
try {
var stream = yield file.replace_async (null, false, GLib.FileCreateFlags.NONE);
yield stream.write_async (contents.data);
}
catch (Error error) {
// TODO GUI infobar
warning ("Failed to create new launcher: %s", error.message);
}
}
internal Launcher (GLib.File file) {
this.file = file;
}
bool init (GLib.Cancellable? cancellable) throws GLib.Error {
if (!file.get_basename ().has_suffix (".desktop"))
return false;
var keyfile = new GLib.KeyFile ();
keyfile.load_from_file (file.get_path (), GLib.KeyFileFlags.NONE);
exec = keyfile.get_string ("Desktop Entry", "Exec");
if (!exec.has_prefix (EXEC_PREFIX))
return false;
name = keyfile.get_string ("Desktop Entry", "Name");
icon_name = keyfile.get_string ("Desktop Entry", "Icon");
uri = exec.replace (EXEC_PREFIX, "");
return true;
}
}
private class Sidebar : Gtk.VBox, Midori.Viewable {
Gtk.Toolbar? toolbar = null;
Gtk.ListStore store = new Gtk.ListStore (1, typeof (Launcher));
Gtk.TreeView treeview;
Katze.Array array;
public unowned string get_stock_id () {
return Midori.Stock.WEB_BROWSER;
}
public unowned string get_label () {
return _("Applications");
}
public Gtk.Widget get_toolbar () {
if (toolbar == null) {
toolbar = new Gtk.Toolbar ();
toolbar.set_icon_size (Gtk.IconSize.BUTTON);
}
return toolbar;
}
public Sidebar (Katze.Array array) {
Gtk.TreeViewColumn column;
treeview = new Gtk.TreeView.with_model (store);
treeview.headers_visible = false;
store.set_sort_column_id (0, Gtk.SortType.ASCENDING);
store.set_sort_func (0, tree_sort_func);
column = new Gtk.TreeViewColumn ();
Gtk.CellRendererPixbuf renderer_icon = new Gtk.CellRendererPixbuf ();
column.pack_start (renderer_icon, false);
column.set_cell_data_func (renderer_icon, on_render_icon);
treeview.append_column (column);
column = new Gtk.TreeViewColumn ();
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
Gtk.CellRendererText renderer_text = new Gtk.CellRendererText ();
column.pack_start (renderer_text, true);
column.set_expand (true);
column.set_cell_data_func (renderer_text, on_render_text);
treeview.append_column (column);
treeview.show ();
pack_start (treeview, true, true, 0);
this.array = array;
array.add_item.connect (launcher_added);
array.remove_item.connect (launcher_removed);
foreach (GLib.Object item in array.get_items ())
launcher_added (item);
}
private int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
Launcher launcher1, launcher2;
model.get (a, 0, out launcher1);
model.get (b, 0, out launcher2);
return strcmp (launcher1.name, launcher2.name);
}
void launcher_added (GLib.Object item) {
var launcher = item as Launcher;
Gtk.TreeIter iter;
store.append (out iter);
store.set (iter, 0, launcher);
}
void launcher_removed (GLib.Object item) {
// TODO remove iter
}
private void on_render_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
Gtk.TreeModel model, Gtk.TreeIter iter) {
Launcher launcher;
model.get (iter, 0, out launcher);
if (launcher.icon_name != null)
renderer.set ("icon-name", launcher.icon_name);
else
renderer.set ("stock-id", Gtk.STOCK_FILE);
renderer.set ("stock-size", Gtk.IconSize.BUTTON,
"xpad", 4);
}
private void on_render_text (Gtk.CellLayout column, Gtk.CellRenderer renderer,
Gtk.TreeModel model, Gtk.TreeIter iter) {
Launcher launcher;
model.get (iter, 0, out launcher);
renderer.set ("markup",
Markup.printf_escaped ("<b>%s</b>\n%s",
launcher.name, launcher.uri),
"ellipsize", Pango.EllipsizeMode.END);
}
}
private class Manager : Midori.Extension {
internal Katze.Array array;
internal GLib.File app_folder;
internal GLib.FileMonitor? monitor;
internal GLib.List<Gtk.Widget> widgets;
void app_changed (GLib.File file, GLib.File? other, GLib.FileMonitorEvent event) {
try {
switch (event) {
case GLib.FileMonitorEvent.DELETED:
// TODO array.remove_item ();
break;
case GLib.FileMonitorEvent.CREATED:
var launcher = new Launcher (file);
if (launcher.init ())
array.add_item (launcher);
break;
case GLib.FileMonitorEvent.CHANGED:
// TODO
break;
}
}
catch (Error error) {
warning ("Application changed: %s", error.message);
}
}
async void populate_apps () {
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ());
app_folder = data_dir.get_child ("applications");
try {
try {
app_folder.make_directory_with_parents (null);
}
catch (IOError folder_error) {
if (!(folder_error is IOError.EXISTS))
throw folder_error;
}
monitor = app_folder.monitor_directory (0, null);
monitor.changed.connect (app_changed);
var enumerator = yield app_folder.enumerate_children_async ("standard::name", 0);
while (true) {
var files = yield enumerator.next_files_async (10);
if (files == null)
break;
foreach (var info in files) {
var desktop_file = app_folder.get_child (info.get_name ());
try {
var launcher = new Launcher (desktop_file);
if (launcher.init ())
array.add_item (launcher);
}
catch (Error error) {
warning ("Failed to parse launcher: %s", error.message);
}
}
}
}
catch (Error io_error) {
monitor = null;
warning ("Failed to list .desktop files (%s): %s",
app_folder.get_path (), io_error.message);
}
}
void tool_menu_populated (Midori.Browser browser, Gtk.Menu menu) {
var menuitem = new Gtk.MenuItem.with_mnemonic (_("Create _Launcher"));
menuitem.show ();
menu.append (menuitem);
menuitem.activate.connect (() => {
var view = browser.tab as Midori.View;
Launcher.create.begin (app_folder,
view.get_display_uri (), view.get_display_title ());
});
}
void browser_added (Midori.Browser browser) {
var viewable = new Sidebar (array);
viewable.show ();
browser.panel.append_page (viewable);
browser.populate_tool_menu.connect (tool_menu_populated);
// TODO website context menu
widgets.append (viewable);
}
void activated (Midori.App app) {
array = new Katze.Array (typeof (Launcher));
populate_apps.begin ();
widgets = new GLib.List<Gtk.Widget> ();
foreach (var browser in app.get_browsers ())
browser_added (browser);
app.add_browser.connect (browser_added);
}
void deactivated () {
var app = get_app ();
if (monitor != null)
monitor.changed.disconnect (app_changed);
app.add_browser.disconnect (browser_added);
foreach (var widget in widgets)
widget.destroy ();
}
internal Manager () {
GLib.Object (name: _("Web App Manager"),
description: _("Manage websites installed as applications"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "Christian Dywan <christian@twotoasts.de>");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
}
public Midori.Extension extension_init () {
return new Apps.Manager ();
}

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de> Copyright (C) 2009-2013 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2010 Samuel Creshal <creshal@arcor.de> Copyright (C) 2010 Samuel Creshal <creshal@arcor.de>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -12,67 +12,34 @@
#include <midori/midori.h> #include <midori/midori.h>
static void static GdkColor
colorful_tabs_modify_fg (GtkWidget* label, view_get_bgcolor_for_hostname (MidoriView* view, gchar* hostname)
GdkColor* color)
{ {
GtkWidget* box = gtk_bin_get_child (GTK_BIN (label));
GList* children = gtk_container_get_children (GTK_CONTAINER (box));
for (; children != NULL; children = g_list_next (children))
{
if (GTK_IS_LABEL (children->data))
{
gtk_widget_modify_fg (children->data, GTK_STATE_ACTIVE, color);
gtk_widget_modify_fg (children->data, GTK_STATE_NORMAL, color);
/* Also modify the label itself, for Tab Panel */
gtk_widget_modify_fg (label, GTK_STATE_NORMAL, color);
break;
}
}
g_list_free (children);
}
static void
colorful_tabs_view_notify_uri_cb (MidoriView* view,
GParamSpec* pspec,
MidoriExtension* extension)
{
GtkWidget* label;
gchar* hostname;
gchar* colorstr;
GdkColor color; GdkColor color;
GdkColor fgcolor; GdkPixbuf* icon = midori_view_get_icon (view);
GdkPixbuf* icon; if (icon != NULL)
label = midori_view_get_proxy_tab_label (view);
if (!midori_uri_is_blank (midori_view_get_display_uri (view))
&& (hostname = midori_uri_parse_hostname (midori_view_get_display_uri (view), NULL))
&& katze_object_get_enum (view, "load-status") == MIDORI_LOAD_FINISHED)
{
icon = midori_view_get_icon (view);
if (midori_view_get_icon_uri (view) != NULL)
{ {
GdkPixbuf* newpix; GdkPixbuf* newpix;
guchar* pixels; guchar* pixels;
newpix = gdk_pixbuf_scale_simple (icon, 1, 1, GDK_INTERP_BILINEAR); newpix = gdk_pixbuf_scale_simple (icon, 1, 1, GDK_INTERP_BILINEAR);
g_return_if_fail (gdk_pixbuf_get_bits_per_sample (newpix) == 8);
pixels = gdk_pixbuf_get_pixels (newpix); pixels = gdk_pixbuf_get_pixels (newpix);
color.red = pixels[0] * 225; color.red = pixels[0] * 255;
color.green = pixels[1] * 225; color.green = pixels[1] * 255;
color.blue = pixels[2] * 225; color.blue = pixels[2] * 255;
} }
else else
{ {
gchar* hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, hostname, 1); gchar* hash, *colorstr;
hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, hostname, 1);
colorstr = g_strndup (hash, 6 + 1); colorstr = g_strndup (hash, 6 + 1);
g_free (hash);
colorstr[0] = '#'; colorstr[0] = '#';
gdk_color_parse (colorstr, &color); gdk_color_parse (colorstr, &color);
g_free (hash);
g_free (colorstr);
} }
g_free (hostname);
if ((color.red < 35000) if ((color.red < 35000)
&& (color.green < 35000) && (color.green < 35000)
@ -83,19 +50,6 @@ colorful_tabs_view_notify_uri_cb (MidoriView* view,
color.blue += 20000; color.blue += 20000;
} }
/* Ensure high contrast by enforcing black/ white text colour. */
if ((color.red < 41000)
&& (color.green < 41000)
&& (color.blue < 41000))
gdk_color_parse ("#fff", &fgcolor);
else
gdk_color_parse ("#000", &fgcolor);
gtk_event_box_set_visible_window (GTK_EVENT_BOX (label), TRUE);
colorful_tabs_modify_fg (label, &fgcolor);
gtk_widget_modify_bg (label, GTK_STATE_NORMAL, &color);
if (color.red < 10000) if (color.red < 10000)
color.red = 5000; color.red = 5000;
else else
@ -109,14 +63,36 @@ colorful_tabs_view_notify_uri_cb (MidoriView* view,
else else
color.green -= 5000; color.green -= 5000;
gtk_widget_modify_bg (label, GTK_STATE_ACTIVE, &color); return color;
}
static void
colorful_tabs_view_notify_uri_cb (MidoriView* view,
GParamSpec* pspec,
MidoriExtension* extension)
{
gchar* hostname;
GdkColor color;
GdkColor fgcolor;
if (!midori_uri_is_blank (midori_view_get_display_uri (view))
&& (hostname = midori_uri_parse_hostname (midori_view_get_display_uri (view), NULL))
&& midori_view_get_icon_uri (view) != NULL)
{
color = view_get_bgcolor_for_hostname (view, hostname);
g_free (hostname);
/* Ensure high contrast by enforcing black/ white text colour. */
if ((color.red < 41000)
&& (color.green < 41000)
&& (color.blue < 41000))
gdk_color_parse ("#fff", &fgcolor);
else
gdk_color_parse ("#000", &fgcolor);
midori_view_set_colors (view, &fgcolor, &color);
} }
else else
{ midori_view_set_colors (view, NULL, NULL);
gtk_widget_modify_bg (label, GTK_STATE_NORMAL, NULL);
gtk_widget_modify_bg (label, GTK_STATE_ACTIVE, NULL);
colorful_tabs_modify_fg (label, NULL);
}
} }
static void static void
@ -138,7 +114,7 @@ static void
colorful_tabs_deactivate_cb (MidoriExtension* extension, colorful_tabs_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser) MidoriBrowser* browser)
{ {
guint i; GList* children;
GtkWidget* view; GtkWidget* view;
MidoriApp* app = midori_extension_get_app (extension); MidoriApp* app = midori_extension_get_app (extension);
@ -148,17 +124,15 @@ colorful_tabs_deactivate_cb (MidoriExtension* extension,
browser, colorful_tabs_browser_add_tab_cb, extension); browser, colorful_tabs_browser_add_tab_cb, extension);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
extension, colorful_tabs_deactivate_cb, browser); extension, colorful_tabs_deactivate_cb, browser);
i = 0;
while ((view = midori_browser_get_nth_tab (browser, i++))) children = midori_browser_get_tabs (MIDORI_BROWSER (browser));
for (; children; children = g_list_next (children))
{ {
GtkWidget* label = midori_view_get_proxy_tab_label (MIDORI_VIEW (view)); midori_view_set_colors (children->data, NULL, NULL);
gtk_event_box_set_visible_window (GTK_EVENT_BOX (label), FALSE);
gtk_widget_modify_bg (label, GTK_STATE_NORMAL, NULL);
gtk_widget_modify_bg (label, GTK_STATE_ACTIVE, NULL);
colorful_tabs_modify_fg (label, NULL);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
view, colorful_tabs_view_notify_uri_cb, extension); children->data, colorful_tabs_view_notify_uri_cb, extension);
} }
g_list_free (children);
} }
static void static void
@ -166,12 +140,13 @@ colorful_tabs_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser, MidoriBrowser* browser,
MidoriExtension* extension) MidoriExtension* extension)
{ {
guint i; GList* children;
GtkWidget* view;
children = midori_browser_get_tabs (MIDORI_BROWSER (browser));
for (; children; children = g_list_next (children))
colorful_tabs_browser_add_tab_cb (browser, children->data, extension);
g_list_free (children);
i = 0;
while ((view = midori_browser_get_nth_tab (browser, i++)))
colorful_tabs_browser_add_tab_cb (browser, view, extension);
g_signal_connect (browser, "add-tab", g_signal_connect (browser, "add-tab",
G_CALLBACK (colorful_tabs_browser_add_tab_cb), extension); G_CALLBACK (colorful_tabs_browser_add_tab_cb), extension);
g_signal_connect (extension, "deactivate", g_signal_connect (extension, "deactivate",

View file

@ -12,7 +12,7 @@
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
#include <midori/midori.h> #include <midori/midori.h>
#include <webkit/webkit.h> #include "katze/katze.h"
#include <time.h> #include <time.h>
#include "cookie-manager.h" #include "cookie-manager.h"
@ -166,7 +166,9 @@ static void cookie_manager_page_cookies_changed_cb(CookieManager *cm, CookieMana
gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview), GTK_TREE_MODEL(priv->filter)); gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview), GTK_TREE_MODEL(priv->filter));
g_object_unref(priv->filter); g_object_unref(priv->filter);
/* if a filter is set, apply it again */ /* if a filter is set, apply it again but ignore the place holder text */
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_has_default"))
{
filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry)); filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry));
if (*filter_text != '\0') if (*filter_text != '\0')
{ {
@ -174,6 +176,7 @@ static void cookie_manager_page_cookies_changed_cb(CookieManager *cm, CookieMana
gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview)); gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview));
} }
} }
}
static void cookie_manager_page_filter_changed_cb(CookieManager *cm, const gchar *text, static void cookie_manager_page_filter_changed_cb(CookieManager *cm, const gchar *text,
@ -576,12 +579,15 @@ static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CookieManager
if (toplevel != NULL) if (toplevel != NULL)
gtk_window_set_icon_name(GTK_WINDOW(dialog), gtk_window_get_icon_name(GTK_WINDOW(toplevel))); gtk_window_set_icon_name(GTK_WINDOW(dialog), gtk_window_get_icon_name(GTK_WINDOW(toplevel)));
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_has_default"))
{
filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry)); filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry));
if (*filter_text != '\0') if (*filter_text != '\0')
{ {
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
_("Only cookies which match the filter will be deleted.")); _("Only cookies which match the filter will be deleted."));
} }
}
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
cm_delete_all_cookies_real(cmp); cm_delete_all_cookies_real(cmp);
@ -650,25 +656,28 @@ static void cm_tree_drag_data_get_cb(GtkWidget *widget, GdkDragContext *drag_con
static gchar *cm_get_cookie_description_text(SoupCookie *cookie) static gchar *cm_get_cookie_description_text(SoupCookie *cookie)
{ {
static gchar date_fmt[512];
gchar *expires; gchar *expires;
gchar *text; gchar *text;
time_t expiration_time;
const struct tm *tm;
g_return_val_if_fail(cookie != NULL, NULL); g_return_val_if_fail(cookie != NULL, NULL);
if (cookie->expires != NULL) if (cookie->expires != NULL)
{ {
expiration_time = soup_date_to_time_t(cookie->expires); time_t expiration_time = soup_date_to_time_t(cookie->expires);
tm = localtime(&expiration_time); #if GLIB_CHECK_VERSION (2, 26, 0)
/* even if some gcc versions complain about "%c", there is nothing wrong with and here we GDateTime* date = g_date_time_new_from_unix_local(expiration_time);
* want to use it */ expires = g_date_time_format(date, "%c");
g_date_time_unref(date);
#else
static gchar date_fmt[512];
const struct tm *tm = localtime(&expiration_time);
/* Some GCC versions falsely complain about "%c" */
strftime(date_fmt, sizeof(date_fmt), "%c", tm); strftime(date_fmt, sizeof(date_fmt), "%c", tm);
expires = date_fmt; expires = g_strdup(date_fmt);
#endif
} }
else else
expires = _("At the end of the session"); expires = g_strdup(_("At the end of the session"));
text = g_markup_printf_escaped( text = g_markup_printf_escaped(
_("<b>Host</b>: %s\n<b>Name</b>: %s\n<b>Value</b>: %s\n<b>Path</b>: %s\n" _("<b>Host</b>: %s\n<b>Name</b>: %s\n<b>Value</b>: %s\n<b>Path</b>: %s\n"
@ -677,8 +686,10 @@ static gchar *cm_get_cookie_description_text(SoupCookie *cookie)
cookie->name, cookie->name,
cookie->value, cookie->value,
cookie->path, cookie->path,
/* i18n: is this cookie secure (SSL)? yes/ no */
cookie->secure ? _("Yes") : _("No"), cookie->secure ? _("Yes") : _("No"),
expires); expires);
g_free(expires);
return text; return text;
} }
@ -702,7 +713,6 @@ static gchar *cm_get_domain_description_text(const gchar *domain, gint cookie_co
} }
#if GTK_CHECK_VERSION(2, 12, 0)
static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode,
GtkTooltip *tooltip, CookieManagerPage *cmp) GtkTooltip *tooltip, CookieManagerPage *cmp)
{ {
@ -731,8 +741,6 @@ static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolea
return FALSE; return FALSE;
} }
#endif
static gboolean cm_filter_match(const gchar *haystack, const gchar *needle) static gboolean cm_filter_match(const gchar *haystack, const gchar *needle)
{ {
@ -812,25 +820,20 @@ static void cm_filter_entry_changed_cb(GtkEditable *editable, CookieManagerPage
if (priv->ignore_changed_filter) if (priv->ignore_changed_filter)
return; return;
if (!g_object_get_data (G_OBJECT (editable), "sokoke_has_default"))
text = gtk_entry_get_text(GTK_ENTRY(editable)); text = gtk_entry_get_text(GTK_ENTRY(editable));
else
text = NULL;
cm_filter_tree(cmp, text); cm_filter_tree(cmp, text);
cookie_manager_update_filter(priv->parent, text); cookie_manager_update_filter(priv->parent, text);
if (*text != '\0') if (text && *text)
gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview));
else
gtk_tree_view_collapse_all(GTK_TREE_VIEW(priv->treeview)); gtk_tree_view_collapse_all(GTK_TREE_VIEW(priv->treeview));
else
gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview));
} }
static void cm_filter_entry_clear_icon_released_cb(GtkIconEntry *e, gint pos, gint btn, gpointer data)
{
if (pos == GTK_ICON_ENTRY_SECONDARY)
gtk_entry_set_text(GTK_ENTRY(e), "");
}
static void cm_tree_selection_changed_cb(GtkTreeSelection *selection, CookieManagerPage *cmp) static void cm_tree_selection_changed_cb(GtkTreeSelection *selection, CookieManagerPage *cmp)
{ {
GList *rows; GList *rows;
@ -1002,6 +1005,7 @@ static void cm_tree_render_text_cb(GtkTreeViewColumn *column, GtkCellRenderer *r
} }
else else
g_object_set(renderer, "text", name, NULL); g_object_set(renderer, "text", name, NULL);
g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
g_free(name); g_free(name);
} }
@ -1022,6 +1026,7 @@ static GtkWidget *cm_tree_prepare(CookieManagerPage *cmp)
renderer = gtk_cell_renderer_text_new(); renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes( column = gtk_tree_view_column_new_with_attributes(
_("Name"), renderer, "text", COOKIE_MANAGER_COL_NAME, NULL); _("Name"), renderer, "text", COOKIE_MANAGER_COL_NAME, NULL);
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_sort_indicator(column, TRUE); gtk_tree_view_column_set_sort_indicator(column, TRUE);
gtk_tree_view_column_set_sort_column_id(column, COOKIE_MANAGER_COL_NAME); gtk_tree_view_column_set_sort_column_id(column, COOKIE_MANAGER_COL_NAME);
gtk_tree_view_column_set_resizable(column, TRUE); gtk_tree_view_column_set_resizable(column, TRUE);
@ -1045,10 +1050,8 @@ static GtkWidget *cm_tree_prepare(CookieManagerPage *cmp)
g_signal_connect(treeview, "popup-menu", G_CALLBACK(cm_tree_popup_menu_cb), cmp); g_signal_connect(treeview, "popup-menu", G_CALLBACK(cm_tree_popup_menu_cb), cmp);
/* tooltips */ /* tooltips */
#if GTK_CHECK_VERSION(2, 12, 0)
gtk_widget_set_has_tooltip(treeview, TRUE); gtk_widget_set_has_tooltip(treeview, TRUE);
g_signal_connect(treeview, "query-tooltip", G_CALLBACK(cm_tree_query_tooltip), cmp); g_signal_connect(treeview, "query-tooltip", G_CALLBACK(cm_tree_query_tooltip), cmp);
#endif
/* drag'n'drop */ /* drag'n'drop */
gtk_tree_view_enable_model_drag_source( gtk_tree_view_enable_model_drag_source(
@ -1100,7 +1103,6 @@ static void cookie_manager_page_init(CookieManagerPage *self)
GtkWidget *desc_swin; GtkWidget *desc_swin;
GtkWidget *paned; GtkWidget *paned;
GtkWidget *filter_hbox; GtkWidget *filter_hbox;
GtkWidget *filter_label;
GtkWidget *treeview; GtkWidget *treeview;
CookieManagerPagePrivate *priv; CookieManagerPagePrivate *priv;
@ -1132,29 +1134,15 @@ static void cookie_manager_page_init(CookieManagerPage *self)
tree_swin = gtk_scrolled_window_new(NULL, NULL); tree_swin = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_swin), gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_swin),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_swin), GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(tree_swin), treeview); gtk_container_add(GTK_CONTAINER(tree_swin), treeview);
gtk_widget_show(tree_swin); gtk_widget_show(tree_swin);
filter_label = gtk_label_new(_("Filter:")); priv->filter_entry = sokoke_search_entry_new (_("Search Cookies by Name or Domain"));
gtk_widget_show(filter_label);
priv->filter_entry = gtk_icon_entry_new();
gtk_widget_set_tooltip_text(priv->filter_entry,
_("Enter a filter string to show only cookies whose name or domain "
"field match the entered filter"));
gtk_widget_show(priv->filter_entry); gtk_widget_show(priv->filter_entry);
gtk_icon_entry_set_icon_from_stock(GTK_ICON_ENTRY(priv->filter_entry),
GTK_ICON_ENTRY_SECONDARY, GTK_STOCK_CLEAR);
gtk_icon_entry_set_icon_highlight(GTK_ICON_ENTRY (priv->filter_entry),
GTK_ICON_ENTRY_SECONDARY, TRUE);
g_signal_connect(priv->filter_entry, "icon-release",
G_CALLBACK(cm_filter_entry_clear_icon_released_cb), NULL);
g_signal_connect(priv->filter_entry, "changed", G_CALLBACK(cm_filter_entry_changed_cb), self); g_signal_connect(priv->filter_entry, "changed", G_CALLBACK(cm_filter_entry_changed_cb), self);
g_signal_connect(priv->filter_entry, "activate", G_CALLBACK(cm_filter_entry_changed_cb), self); g_signal_connect(priv->filter_entry, "activate", G_CALLBACK(cm_filter_entry_changed_cb), self);
filter_hbox = gtk_hbox_new(FALSE, 0); filter_hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(filter_hbox), filter_label, FALSE, FALSE, 3);
gtk_box_pack_start(GTK_BOX(filter_hbox), priv->filter_entry, TRUE, TRUE, 3); gtk_box_pack_start(GTK_BOX(filter_hbox), priv->filter_entry, TRUE, TRUE, 3);
gtk_widget_show(filter_hbox); gtk_widget_show(filter_hbox);

View file

@ -11,7 +11,7 @@
#include "config.h" #include "config.h"
#include <midori/midori.h> #include <midori/midori.h>
#include <webkit/webkit.h> #include "katze/katze.h"
#include "cookie-manager.h" #include "cookie-manager.h"
#include "cookie-manager-page.h" #include "cookie-manager-page.h"
@ -234,7 +234,8 @@ static void cookie_manager_jar_changed_cb(SoupCookieJar *jar, SoupCookie *old, S
/* We delay these events a little bit to avoid too many rebuilds of the tree. /* We delay these events a little bit to avoid too many rebuilds of the tree.
* Some websites (like Flyspray bugtrackers sent a whole bunch of cookies at once. */ * Some websites (like Flyspray bugtrackers sent a whole bunch of cookies at once. */
if (priv->timer_id == 0) if (priv->timer_id == 0)
priv->timer_id = g_timeout_add_seconds(1, (GSourceFunc) cookie_manager_delayed_refresh, cm); priv->timer_id = midori_timeout_add_seconds(
1, (GSourceFunc) cookie_manager_delayed_refresh, cm, NULL);
} }

View file

@ -0,0 +1,805 @@
/*
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
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.
*/
#include "cookie-permission-manager-preferences-window.h"
/* Define this class in GObject system */
G_DEFINE_TYPE(CookiePermissionManagerPreferencesWindow,
cookie_permission_manager_preferences_window,
GTK_TYPE_DIALOG)
/* Properties */
enum
{
PROP_0,
PROP_MANAGER,
PROP_LAST
};
static GParamSpec* CookiePermissionManagerPreferencesWindowProperties[PROP_LAST]={ 0, };
/* Private structure - access only by public API if needed */
#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowPrivate))
struct _CookiePermissionManagerPreferencesWindowPrivate
{
/* Extension related */
CookiePermissionManager *manager;
sqlite3 *database;
/* Dialog related */
GtkWidget *contentArea;
GtkListStore *listStore;
GtkWidget *list;
GtkTreeSelection *listSelection;
GtkWidget *deleteButton;
GtkWidget *deleteAllButton;
GtkWidget *askForUnknownPolicyCheckbox;
GtkWidget *addDomainEntry;
GtkWidget *addDomainPolicyCombo;
GtkWidget *addDomainButton;
gint signalManagerChangedDatabaseID;
gint signalManagerAskForUnknownPolicyID;
gint signalAskForUnknownPolicyID;
};
enum
{
DOMAIN_COLUMN,
POLICY_COLUMN,
N_COLUMN
};
/* IMPLEMENTATION: Private variables and methods */
/* "Add domain"-button was pressed */
static void _cookie_permission_manager_preferences_on_add_domain_clicked(CookiePermissionManagerPreferencesWindow *self,
gpointer *inUserData)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
gchar *domain;
const gchar *domainStart, *domainEnd;
gchar *realDomain;
GtkTreeIter policyIter;
g_return_if_fail(priv->database);
/* Get domain name entered */
domain=g_hostname_to_ascii(gtk_entry_get_text(GTK_ENTRY(priv->addDomainEntry)));
/* Trim whitespaces from start and end of entered domain name */
domainStart=domain;
while(*domainStart && g_ascii_isspace(*domainStart)) domainStart++;
domainEnd=domain+strlen(domain)-1;
while(*domainEnd && g_ascii_isspace(*domainEnd)) domainEnd--;
if(domainEnd<=domainStart) return;
/* Seperate domain name from whitespaces */
realDomain=g_strndup(domain, domainEnd-domainStart+1);
if(!realDomain) return;
/* Get policy from combo box */
if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->addDomainPolicyCombo), &policyIter))
{
gchar *sql;
gchar *error=NULL;
gint success;
gint policy;
gchar *policyName;
/* Get policy value to set for domain */
gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(priv->addDomainPolicyCombo)),
&policyIter,
0, &policy,
1, &policyName,
-1);
/* Add domain name and the selected policy to database */
sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (domain, value) VALUES ('%q', %d);",
realDomain,
policy);
success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
/* Show error message if any */
if(success==SQLITE_OK)
{
gtk_list_store_append(priv->listStore, &policyIter);
gtk_list_store_set(priv->listStore,
&policyIter,
DOMAIN_COLUMN, realDomain,
POLICY_COLUMN, policyName,
-1);
}
else g_warning(_("SQL fails: %s"), error);
if(error) sqlite3_free(error);
/* Free allocated resources */
sqlite3_free(sql);
}
/* Free allocated resources */
g_free(realDomain);
g_free(domain);
}
/* Entry containing domain name which may be added to list has changed */
static void _cookie_permission_manager_preferences_on_add_domain_entry_changed(CookiePermissionManagerPreferencesWindow *self,
GtkEditable *inEditable)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
gchar *asciiDomain, *checkAsciiDomain;
gchar *asciiDomainStart, *asciiDomainEnd;
gint dots;
gboolean isValid=FALSE;
/* Get ASCII representation of domain name entered */
asciiDomain=g_hostname_to_ascii(gtk_entry_get_text(GTK_ENTRY(priv->addDomainEntry)));
/* Trim whitespaces from start and end of entered domain name */
asciiDomainStart=asciiDomain;
while(*asciiDomainStart && g_ascii_isspace(*asciiDomainStart)) asciiDomainStart++;
asciiDomainEnd=asciiDomain+strlen(asciiDomain)-1;
while(*asciiDomainEnd && g_ascii_isspace(*asciiDomainEnd)) asciiDomainEnd--;
/* We allow only domain names and not cookie domain name so entered name
* must not start with a dot
*/
checkAsciiDomain=asciiDomainStart;
isValid=(*asciiDomainStart!='.' && *asciiDomainEnd!='.');
/* Now check if ASCII domain name is valid (very very simple check)
* and contains a hostname besides TLD
*/
dots=0;
while(*checkAsciiDomain &&
checkAsciiDomain<=asciiDomainEnd &&
isValid)
{
/* Check for dot as (at least the first one) seperates hostname from TLD */
if(*checkAsciiDomain=='.') dots++;
else
{
/* Check for valid characters in domain name.
* Valid domain name can only contain ASCII alphabetic letters,
* digits (0-9) and hyphens ('-')
*/
isValid=(g_ascii_isalpha(*checkAsciiDomain) ||
g_ascii_isdigit(*checkAsciiDomain) ||
*checkAsciiDomain=='-');
}
checkAsciiDomain++;
}
/* If we have not reached the trimmed end of string something must have gone wrong
* and domain entered is invalid. If domain name entered excluding dots is longer
* than 255 character it is also invalid.
*/
if(checkAsciiDomain<asciiDomainEnd) isValid=FALSE;
else if((checkAsciiDomain-asciiDomainStart-dots)>255) isValid=FALSE;
/* We need at least one dot in domain name (minimum number of dots to seperate
* hostname from TLD)
*/
isValid=(isValid && dots>0);
/* Activate "add" button if hostname (equal to domain name here) is valid */
gtk_widget_set_sensitive(priv->addDomainButton, isValid);
/* Free allocated resources */
g_free(asciiDomain);
}
/* Fill domain list with stored policies */
static void _cookie_permission_manager_preferences_window_fill(CookiePermissionManagerPreferencesWindow *self)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
gint success;
sqlite3_stmt *statement=NULL;
/* Clear tree/list view */
gtk_list_store_clear(priv->listStore);
/* If no database is present return here */
if(!priv->database) return;
/* Fill list store with policies from database */
success=sqlite3_prepare_v2(priv->database,
"SELECT domain, value FROM policies;",
-1,
&statement,
NULL);
if(statement && success==SQLITE_OK)
{
gchar *domain;
gint policy;
gchar *policyName;
GtkTreeIter iter;
while(sqlite3_step(statement)==SQLITE_ROW)
{
/* Get values */
domain=(gchar*)sqlite3_column_text(statement, 0);
policy=sqlite3_column_int(statement, 1);
switch(policy)
{
case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT:
policyName=_("Accept");
break;
case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION:
policyName=_("Accept for session");
break;
case COOKIE_PERMISSION_MANAGER_POLICY_BLOCK:
policyName=_("Block");
break;
default:
policyName=NULL;
break;
}
if(policyName)
{
gtk_list_store_append(priv->listStore, &iter);
gtk_list_store_set(priv->listStore,
&iter,
DOMAIN_COLUMN, domain,
POLICY_COLUMN, policyName,
-1);
}
}
}
else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
sqlite3_finalize(statement);
}
/* Database instance in manager changed */
static void _cookie_permission_manager_preferences_window_manager_database_changed(CookiePermissionManagerPreferencesWindow *self,
GParamSpec *inSpec,
gpointer inUserData)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
CookiePermissionManager *manager=COOKIE_PERMISSION_MANAGER(inUserData);
const gchar *databaseFilename;
/* Close connection to any open database */
if(priv->database) sqlite3_close(priv->database);
priv->database=NULL;
/* Get pointer to new database and open database */
g_object_get(manager, "database-filename", &databaseFilename, NULL);
if(databaseFilename)
{
gint success;
success=sqlite3_open(databaseFilename, &priv->database);
if(success!=SQLITE_OK)
{
g_warning(_("Could not open database of extenstion: %s"), sqlite3_errmsg(priv->database));
if(priv->database) sqlite3_close(priv->database);
priv->database=NULL;
}
}
/* Fill list with new database */
_cookie_permission_manager_preferences_window_fill(self);
/* Set up availability of management buttons */
gtk_widget_set_sensitive(priv->deleteAllButton, priv->database!=NULL);
gtk_widget_set_sensitive(priv->list, priv->database!=NULL);
return;
}
/* Ask-for-unknown-policy in manager changed or check-box changed */
static void _cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
GParamSpec *inSpec,
gpointer inUserData)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
CookiePermissionManager *manager=COOKIE_PERMISSION_MANAGER(inUserData);
gboolean doAsk;
/* Get new ask-for-unknown-policy value */
g_object_get(manager, "ask-for-unknown-policy", &doAsk, NULL);
/* Set toogle in widget (but block signal for toggle) */
g_signal_handler_block(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox), doAsk);
g_signal_handler_unblock(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
}
static void _cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
gpointer *inUserData)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
gboolean doAsk;
/* Get toogle state of widget (but block signal for manager) and set in manager */
g_signal_handler_block(priv->manager, priv->signalManagerAskForUnknownPolicyID);
doAsk=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox));
g_object_set(priv->manager, "ask-for-unknown-policy", doAsk, NULL);
g_signal_handler_unblock(priv->manager, priv->signalManagerAskForUnknownPolicyID);
}
/* Selection in list changed */
void _cookie_permission_manager_preferences_changed_selection(CookiePermissionManagerPreferencesWindow *self,
GtkTreeSelection *inSelection)
{
gboolean selected=(gtk_tree_selection_count_selected_rows(inSelection)>0 ? TRUE: FALSE);
gtk_widget_set_sensitive(self->priv->deleteButton, selected);
}
/* Delete button was clicked on selection */
void _cookie_permission_manager_preferences_on_delete_selection(CookiePermissionManagerPreferencesWindow *self,
GtkButton *inButton)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
GList *rows, *row, *refs=NULL;
GtkTreeRowReference *ref;
GtkTreeModel *model=GTK_TREE_MODEL(priv->listStore);
GtkTreeIter iter;
GtkTreePath *path;
gchar *domain;
gchar *sql;
gint success;
gchar *error;
/* Get selected rows in list and create a row reference because
* we will modify the model while iterating through selected rows
*/
rows=gtk_tree_selection_get_selected_rows(priv->listSelection, &model);
for(row=rows; row; row=row->next)
{
ref=gtk_tree_row_reference_new(model, (GtkTreePath*)row->data);
refs=g_list_prepend(refs, ref);
}
g_list_foreach(rows,(GFunc)gtk_tree_path_free, NULL);
g_list_free(rows);
/* Delete each selected row by its reference */
for(row=refs; row; row=row->next)
{
/* Get domain from selected row */
path=gtk_tree_row_reference_get_path((GtkTreeRowReference*)row->data);
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, DOMAIN_COLUMN, &domain, -1);
/* Delete domain from database */
sql=sqlite3_mprintf("DELETE FROM policies WHERE domain='%q';", domain);
success=sqlite3_exec(priv->database,
sql,
NULL,
NULL,
&error);
if(success!=SQLITE_OK || error)
{
if(error)
{
g_critical(_("Failed to execute database statement: %s"), error);
sqlite3_free(error);
}
else g_critical(_("Failed to execute database statement: %s"), sqlite3_errmsg(priv->database));
}
sqlite3_free(sql);
/* Delete row from model */
gtk_list_store_remove(priv->listStore, &iter);
}
g_list_foreach(refs,(GFunc)gtk_tree_row_reference_free, NULL);
g_list_free(refs);
}
/* Delete all button was clicked */
void _cookie_permission_manager_preferences_on_delete_all(CookiePermissionManagerPreferencesWindow *self,
GtkButton *inButton)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
gint success;
gchar *error=NULL;
GtkWidget *dialog;
gint dialogResponse;
/* Ask user if he really wants to delete all permissions */
dialog=gtk_message_dialog_new(GTK_WINDOW(self),
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
_("Do you really want to delete all cookie permissions?"));
gtk_window_set_title(GTK_WINDOW(dialog), _("Delete all cookie permissions?"));
gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PROPERTIES);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
_("This action will delete all cookie permissions. "
"You will be asked for permissions again for each web site visited."));
dialogResponse=gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
if(dialogResponse==GTK_RESPONSE_NO) return;
/* Delete all permission */
success=sqlite3_exec(priv->database,
"DELETE FROM policies;",
NULL,
NULL,
&error);
if(success!=SQLITE_OK || error)
{
if(error)
{
g_critical(_("Failed to execute database statement: %s"), error);
sqlite3_free(error);
}
}
/* Re-setup list */
_cookie_permission_manager_preferences_window_fill(self);
}
/* Sorting callbacks */
static gint _cookie_permission_manager_preferences_sort_string_callback(GtkTreeModel *inModel,
GtkTreeIter *inLeft,
GtkTreeIter *inRight,
gpointer inUserData)
{
gchar *left, *right;
gint column=GPOINTER_TO_INT(inUserData);
gint result;
gtk_tree_model_get(inModel, inLeft, column, &left, -1);
gtk_tree_model_get(inModel, inRight, column, &right, -1);
result=g_strcmp0(left, right);
g_free(left);
g_free(right);
return(result);
}
/* IMPLEMENTATION: GObject */
/* Finalize this object */
static void cookie_permission_manager_preferences_window_finalize(GObject *inObject)
{
CookiePermissionManagerPreferencesWindowPrivate *priv=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject)->priv;
/* Dispose allocated resources */
if(priv->database) sqlite3_close(priv->database);
priv->database=NULL;
if(priv->manager)
{
if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
priv->signalManagerChangedDatabaseID=0;
if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
priv->signalManagerAskForUnknownPolicyID=0;
g_object_unref(priv->manager);
priv->manager=NULL;
}
/* Call parent's class finalize method */
G_OBJECT_CLASS(cookie_permission_manager_preferences_window_parent_class)->finalize(inObject);
}
/* Set/get properties */
static void cookie_permission_manager_preferences_window_set_property(GObject *inObject,
guint inPropID,
const GValue *inValue,
GParamSpec *inSpec)
{
CookiePermissionManagerPreferencesWindow *self=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject);
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
GObject *manager;
switch(inPropID)
{
/* Construct-only properties */
case PROP_MANAGER:
/* Release old manager if available and disconnect signals */
if(priv->manager)
{
if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
priv->signalManagerChangedDatabaseID=0;
if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
priv->signalManagerAskForUnknownPolicyID=0;
g_object_unref(priv->manager);
priv->manager=NULL;
}
/* Set new cookie permission manager and
* listen to changes in database property
*/
manager=g_value_get_object(inValue);
if(manager)
{
priv->manager=g_object_ref(manager);
priv->signalManagerChangedDatabaseID=
g_signal_connect_swapped(priv->manager,
"notify::database-filename",
G_CALLBACK(_cookie_permission_manager_preferences_window_manager_database_changed),
self);
_cookie_permission_manager_preferences_window_manager_database_changed(self, NULL, priv->manager);
priv->signalManagerAskForUnknownPolicyID=
g_signal_connect_swapped(priv->manager,
"notify::ask-for-unknown-policy",
G_CALLBACK(_cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed),
self);
_cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(self, NULL, priv->manager);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
break;
}
}
static void cookie_permission_manager_preferences_window_get_property(GObject *inObject,
guint inPropID,
GValue *outValue,
GParamSpec *inSpec)
{
CookiePermissionManagerPreferencesWindow *self=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject);
switch(inPropID)
{
case PROP_MANAGER:
g_value_set_object(outValue, self->priv->manager);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
break;
}
}
/* Class initialization
* Override functions in parent classes and define properties and signals
*/
static void cookie_permission_manager_preferences_window_class_init(CookiePermissionManagerPreferencesWindowClass *klass)
{
GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
/* Override functions */
gobjectClass->finalize=cookie_permission_manager_preferences_window_finalize;
gobjectClass->set_property=cookie_permission_manager_preferences_window_set_property;
gobjectClass->get_property=cookie_permission_manager_preferences_window_get_property;
/* Set up private structure */
g_type_class_add_private(klass, sizeof(CookiePermissionManagerPreferencesWindowPrivate));
/* Define properties */
CookiePermissionManagerPreferencesWindowProperties[PROP_MANAGER]=
g_param_spec_object("manager",
_("Cookie permission manager"),
_("Instance of current cookie permission manager"),
TYPE_COOKIE_PERMISSION_MANAGER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties(gobjectClass, PROP_LAST, CookiePermissionManagerPreferencesWindowProperties);
}
/* Object initialization
* Create private structure and set up default values
*/
static void cookie_permission_manager_preferences_window_init(CookiePermissionManagerPreferencesWindow *self)
{
CookiePermissionManagerPreferencesWindowPrivate *priv;
GtkTreeSortable *sortableList;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *widget;
gchar *text;
gchar *dialogTitle;
GtkWidget *scrolled;
GtkWidget *vbox;
GtkWidget *hbox;
gint width, height;
GtkListStore *list;
GtkTreeIter listIter;
priv=self->priv=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_PRIVATE(self);
/* Set up default values */
priv->manager=NULL;
/* Get content area to add gui controls to */
priv->contentArea=gtk_dialog_get_content_area(GTK_DIALOG(self));
#ifdef HAVE_GTK3
vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
#else
vbox=gtk_vbox_new(FALSE, 0);
#endif
/* Set up dialog */
dialogTitle=_("Configure cookie permission");
gtk_window_set_title(GTK_WINDOW(self), dialogTitle);
gtk_window_set_icon_name(GTK_WINDOW(self), GTK_STOCK_PROPERTIES);
sokoke_widget_get_text_size(GTK_WIDGET(self), "M", &width, &height);
gtk_window_set_default_size(GTK_WINDOW(self), width*52, -1);
widget=sokoke_xfce_header_new(gtk_window_get_icon_name(GTK_WINDOW(self)), dialogTitle);
if(widget) gtk_box_pack_start(GTK_BOX(priv->contentArea), widget, FALSE, FALSE, 0);
gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
/* Set up description */
widget=gtk_label_new(NULL);
text=g_strdup_printf(_("Below is a list of all web sites and the policy set for them. "
"You can delete policies by marking the entries and clicking on <i>Delete</i>."
"You can also add a policy for a domain manually by entering the domain below, "
"choosing the policy and clicking on <i>Add</i>."));
gtk_label_set_markup(GTK_LABEL(widget), text);
g_free(text);
gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 4);
/* Set up model for cookie domain list */
priv->listStore=gtk_list_store_new(N_COLUMN,
G_TYPE_STRING, /* DOMAIN_COLUMN */
G_TYPE_STRING /* POLICY_COLUMN */);
sortableList=GTK_TREE_SORTABLE(priv->listStore);
gtk_tree_sortable_set_sort_func(sortableList,
DOMAIN_COLUMN,
(GtkTreeIterCompareFunc)_cookie_permission_manager_preferences_sort_string_callback,
GINT_TO_POINTER(DOMAIN_COLUMN),
NULL);
gtk_tree_sortable_set_sort_func(sortableList,
POLICY_COLUMN,
(GtkTreeIterCompareFunc)_cookie_permission_manager_preferences_sort_string_callback,
GINT_TO_POINTER(POLICY_COLUMN),
NULL);
gtk_tree_sortable_set_sort_column_id(sortableList, DOMAIN_COLUMN, GTK_SORT_ASCENDING);
/* Set up domain addition widgets */
#ifdef HAVE_GTK3
hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
#else
hbox=gtk_hbox_new(FALSE, 0);
#endif
priv->addDomainEntry=gtk_entry_new();
gtk_entry_set_max_length(GTK_ENTRY(priv->addDomainEntry), 64);
gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainEntry);
g_signal_connect_swapped(priv->addDomainEntry, "changed", G_CALLBACK(_cookie_permission_manager_preferences_on_add_domain_entry_changed), self);
list=gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
gtk_list_store_append(list, &listIter);
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT, 1, _("Accept"), -1);
gtk_list_store_append(list, &listIter);
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION, 1, _("Accept for session"), -1);
gtk_list_store_append(list, &listIter);
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_BLOCK, 1, _("Block"), -1);
priv->addDomainPolicyCombo=gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
gtk_combo_box_set_active(GTK_COMBO_BOX(priv->addDomainPolicyCombo), 0);
gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainPolicyCombo);
renderer=gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->addDomainPolicyCombo), renderer, TRUE);
gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(priv->addDomainPolicyCombo), renderer, "text", 1);
priv->addDomainButton=gtk_button_new_from_stock(GTK_STOCK_ADD);
gtk_widget_set_sensitive(priv->addDomainButton, FALSE);
gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainButton);
g_signal_connect_swapped(priv->addDomainButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_add_domain_clicked), self);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
/* Set up cookie domain list */
priv->list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->listStore));
#ifndef HAVE_GTK3
gtk_widget_set_size_request(priv->list, -1, 300);
#endif
priv->listSelection=gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->list));
gtk_tree_selection_set_mode(priv->listSelection, GTK_SELECTION_MULTIPLE);
g_signal_connect_swapped(priv->listSelection, "changed", G_CALLBACK(_cookie_permission_manager_preferences_changed_selection), self);
renderer=gtk_cell_renderer_text_new();
column=gtk_tree_view_column_new_with_attributes(_("Domain"),
renderer,
"text", DOMAIN_COLUMN,
NULL);
gtk_tree_view_column_set_sort_column_id(column, DOMAIN_COLUMN);
gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
renderer=gtk_cell_renderer_text_new();
column=gtk_tree_view_column_new_with_attributes(_("Policy"),
renderer,
"text", POLICY_COLUMN,
NULL);
gtk_tree_view_column_set_sort_column_id(column, POLICY_COLUMN);
gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
scrolled=gtk_scrolled_window_new(NULL, NULL);
#ifdef HAVE_GTK3
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), height*10);
#endif
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(scrolled), priv->list);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 5);
/* Set up cookie domain list management buttons */
#ifdef HAVE_GTK3
hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
#else
hbox=gtk_hbox_new(FALSE, 0);
#endif
priv->deleteButton=gtk_button_new_from_stock(GTK_STOCK_DELETE);
gtk_widget_set_sensitive(priv->deleteButton, FALSE);
gtk_container_add(GTK_CONTAINER(hbox), priv->deleteButton);
g_signal_connect_swapped(priv->deleteButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_delete_selection), self);
priv->deleteAllButton=gtk_button_new_with_mnemonic(_("Delete _all"));
gtk_button_set_image(GTK_BUTTON(priv->deleteAllButton), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON));
gtk_widget_set_sensitive(priv->deleteAllButton, FALSE);
gtk_container_add(GTK_CONTAINER(hbox), priv->deleteAllButton);
g_signal_connect_swapped(priv->deleteAllButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_delete_all), self);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
/* Add "ask-for-unknown-policy" checkbox */
priv->askForUnknownPolicyCheckbox=gtk_check_button_new_with_mnemonic(_("A_sk for policy if unknown for a domain"));
priv->signalAskForUnknownPolicyID=g_signal_connect_swapped(priv->askForUnknownPolicyCheckbox,
"toggled",
G_CALLBACK(_cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed),
self);
gtk_box_pack_start(GTK_BOX(vbox), priv->askForUnknownPolicyCheckbox, TRUE, TRUE, 5);
/* Finalize setup of content area */
gtk_container_add(GTK_CONTAINER(priv->contentArea), vbox);
}
/* Implementation: Public API */
/* Create new object */
GtkWidget* cookie_permission_manager_preferences_window_new(CookiePermissionManager *inManager)
{
return(g_object_new(TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW,
"manager", inManager,
NULL));
}

View file

@ -0,0 +1,55 @@
/*
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
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.
*/
#ifndef __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__
#define __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__
#include "config.h"
#include <midori/midori.h>
#include "cookie-permission-manager.h"
G_BEGIN_DECLS
#define TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW (cookie_permission_manager_preferences_window_get_type())
#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindow))
#define IS_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW))
#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowClass))
#define IS_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW))
#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowClass))
typedef struct _CookiePermissionManagerPreferencesWindow CookiePermissionManagerPreferencesWindow;
typedef struct _CookiePermissionManagerPreferencesWindowClass CookiePermissionManagerPreferencesWindowClass;
typedef struct _CookiePermissionManagerPreferencesWindowPrivate CookiePermissionManagerPreferencesWindowPrivate;
struct _CookiePermissionManagerPreferencesWindow
{
/* Parent instance */
GtkDialog parent_instance;
/* Private structure */
CookiePermissionManagerPreferencesWindowPrivate *priv;
};
struct _CookiePermissionManagerPreferencesWindowClass
{
/* Parent class */
GtkDialogClass parent_class;
};
/* Public API */
GType cookie_permission_manager_preferences_window_get_type(void);
GtkWidget* cookie_permission_manager_preferences_window_new(CookiePermissionManager *inManager);
G_END_DECLS
#endif /* __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
/*
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
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.
*/
#ifndef __COOKIE_PERMISSION_MANAGER__
#define __COOKIE_PERMISSION_MANAGER__
#include "config.h"
#include <midori/midori.h>
#define COOKIE_PERMISSION_DATABASE "domains.db"
G_BEGIN_DECLS
/* Cookie permission manager enums */
typedef enum
{
COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED,
COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT,
COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION,
COOKIE_PERMISSION_MANAGER_POLICY_BLOCK
} CookiePermissionManagerPolicy;
/* Cookie permission manager object */
#define TYPE_COOKIE_PERMISSION_MANAGER (cookie_permission_manager_get_type())
#define COOKIE_PERMISSION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManager))
#define IS_COOKIE_PERMISSION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COOKIE_PERMISSION_MANAGER))
#define COOKIE_PERMISSION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManagerClass))
#define IS_COOKIE_PERMISSION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COOKIE_PERMISSION_MANAGER))
#define COOKIE_PERMISSION_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManagerClass))
typedef struct _CookiePermissionManager CookiePermissionManager;
typedef struct _CookiePermissionManagerClass CookiePermissionManagerClass;
typedef struct _CookiePermissionManagerPrivate CookiePermissionManagerPrivate;
struct _CookiePermissionManager
{
/* Parent instance */
GObject parent_instance;
/* Private structure */
CookiePermissionManagerPrivate *priv;
};
struct _CookiePermissionManagerClass
{
/* Parent class */
GObjectClass parent_class;
};
/* Public API */
GType cookie_permission_manager_get_type(void);
CookiePermissionManager* cookie_permission_manager_new(MidoriExtension *inExtension, MidoriApp *inApp);
gboolean cookie_permission_manager_get_ask_for_unknown_policy(CookiePermissionManager *self);
void cookie_permission_manager_set_ask_for_unknown_policy(CookiePermissionManager *self, gboolean inDoAsk);
/* Enumeration */
GType cookie_permission_manager_policy_get_type(void) G_GNUC_CONST;
#define COOKIE_PERMISSION_MANAGER_TYPE_POLICY (cookie_permission_manager_policy_get_type())
G_END_DECLS
#endif /* __COOKIE_PERMISSION_MANAGER__ */

View file

@ -0,0 +1,76 @@
/*
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
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.
*/
#include "cookie-permission-manager.h"
#include "cookie-permission-manager-preferences-window.h"
/* Global instance */
CookiePermissionManager *cpm=NULL;
/* This extension was activated */
static void _cpm_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpointer inUserData)
{
g_return_if_fail(cpm==NULL);
cpm=cookie_permission_manager_new(inExtension, inApp);
g_object_set(cpm, "ask-for-unknown-policy", midori_extension_get_boolean(inExtension, "ask-for-unknown-policy"), NULL);
}
/* This extension was deactivated */
static void _cpm_on_deactivate(MidoriExtension *inExtension, gpointer inUserData)
{
g_return_if_fail(cpm);
g_object_unref(cpm);
cpm=NULL;
}
/* Preferences of this extension should be opened */
static void _cpm_on_open_preferences_response(GtkWidget* inDialog,
gint inResponse,
MidoriExtension* inExtension)
{
gtk_widget_destroy(inDialog);
}
static void _cpm_on_open_preferences(MidoriExtension *inExtension)
{
g_return_if_fail(cpm);
/* Show preferences window */
GtkWidget* dialog;
dialog=cookie_permission_manager_preferences_window_new(cpm);
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
g_signal_connect(dialog, "response", G_CALLBACK (_cpm_on_open_preferences_response), inExtension);
gtk_widget_show_all(dialog);
}
/* Main entry for extension */
MidoriExtension *extension_init(void)
{
/* Set up extension */
MidoriExtension *extension=g_object_new(MIDORI_TYPE_EXTENSION,
"name", _("Cookie Security Manager"),
"description", _("Manage cookie permission per site"),
"version", "0.1" MIDORI_VERSION_SUFFIX,
"authors", "Stephan Haller <nomad@froevel.de>",
NULL);
midori_extension_install_boolean(extension, "ask-for-unknown-policy", TRUE);
midori_extension_install_boolean(extension, "show-details-when-ask", FALSE);
g_signal_connect(extension, "activate", G_CALLBACK(_cpm_on_activate), NULL);
g_signal_connect(extension, "deactivate", G_CALLBACK(_cpm_on_deactivate), NULL);
g_signal_connect(extension, "open-preferences", G_CALLBACK(_cpm_on_open_preferences), NULL);
return(extension);
}

View file

@ -28,6 +28,7 @@ copy_tabs_apply_cb (GtkWidget* menuitem,
} }
gtk_clipboard_set_text (clipboard, text->str, -1); gtk_clipboard_set_text (clipboard, text->str, -1);
g_string_free (text, TRUE); g_string_free (text, TRUE);
g_list_free (children);
} }
static void static void

View file

@ -0,0 +1,232 @@
/*
Copyright (C) 2012-2013 André Stösel <andre@stoesel.de>
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.
*/
using Gtk;
using Katze;
using Midori;
namespace DelayedLoad {
private class PreferencesDialog : Gtk.Dialog {
protected Manager dl_manager;
protected Scale slider;
public PreferencesDialog (Manager manager) {
this.dl_manager = manager;
this.title = _("Preferences for %s").printf ( _("Delayed load"));
if (this.get_class ().find_property ("has-separator") != null)
this.set ("has-separator", false);
this.border_width = 5;
this.set_modal (true);
this.set_default_size (350, 100);
this.create_widgets ();
this.response.connect (response_cb);
}
private void response_cb (Gtk.Dialog source, int response_id) {
switch (response_id) {
case ResponseType.APPLY:
this.dl_manager.set_integer ("delay", (int) (this.slider.get_value () * 1000));
this.dl_manager.preferences_changed ();
this.destroy ();
break;
case ResponseType.CANCEL:
this.destroy ();
break;
}
}
private void create_widgets () {
Label text = new Label (_("Delay in seconds until loading the page:"));
#if HAVE_GTK3
this.slider = new Scale.with_range (Orientation.HORIZONTAL, 0, 15, 0.1);
#else
this.slider = new HScale.with_range (0, 15, 0.1);
#endif
int delay = this.dl_manager.get_integer ("delay");
if (delay > 0)
this.slider.set_value ((float)delay / 1000);
#if HAVE_GTK3
Gtk.Box vbox = get_content_area () as Gtk.Box;
vbox.pack_start (text, false, false, 0);
vbox.pack_start (this.slider, false, true, 0);
#else
this.vbox.pack_start (text, false, false, 0);
this.vbox.pack_start (this.slider, false, true, 0);
#endif
this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);
this.show_all ();
}
}
private class TabShaker : GLib.Object {
public unowned Midori.Browser browser;
public GLib.PtrArray tasks;
public bool reload_tab () {
if (tasks.len == 1) {
Midori.View? view = browser.tab as Midori.View;
Midori.View scheduled_view = tasks.index (0) as Midori.View;
if (scheduled_view == view) {
Katze.Item item = view.get_proxy_item ();
item.ref();
int64 delay = item.get_meta_integer ("delay");
if (delay == Midori.Delay.PENDING_UNDELAY) {
view.reload (true);
}
}
}
tasks.remove_index (0);
return false;
}
public TabShaker (Midori.Browser browser) {
this.browser = browser;
}
construct {
this.tasks = new GLib.PtrArray ();
}
}
private class Manager : Midori.Extension {
private int timeout = 0;
private bool initialized = false;
private HashTable<Midori.Browser, TabShaker> tasks;
public signal void preferences_changed ();
private void preferences_changed_cb () {
this.timeout = get_integer ("delay");
}
private void show_preferences () {
PreferencesDialog dialog = new PreferencesDialog (this);
dialog.show ();
}
private void schedule_reload (Midori.Browser browser, Midori.View view) {
if (this.timeout == 0)
view.reload (true);
else {
unowned TabShaker shaker = tasks.get (browser);
if (shaker != null) {
shaker.tasks.add (view);
Midori.Timeout.add (this.timeout, shaker.reload_tab);
}
}
}
private void tab_changed (Midori.View? old_view, Midori.View? new_view) {
if (new_view != null) {
Midori.App app = get_app ();
Midori.Browser browser = app.browser;
Katze.Item item = new_view.get_proxy_item ();
item.ref();
int64 delay = item.get_meta_integer ("delay");
if (delay == Midori.Delay.PENDING_UNDELAY && new_view.progress < 1.0 && this.initialized) {
this.schedule_reload (browser, new_view);
}
}
}
private bool reload_first_tab () {
Midori.App app = get_app ();
Midori.Browser? browser = app.browser;
Midori.View? view = browser.tab as Midori.View;
if (view != null) {
this.initialized = true;
Katze.Item item = view.get_proxy_item ();
item.ref();
int64 delay = item.get_meta_integer ("delay");
if (delay != Midori.Delay.DELAYED) {
if (view.load_status == Midori.LoadStatus.FINISHED) {
if (this.timeout != 0)
this.tasks.set (browser, new TabShaker (browser));
if (view.progress < 1.0)
this.schedule_reload (browser, view);
return false;
}
}
}
return true;
}
private void browser_added (Midori.Browser browser) {
browser.switch_tab.connect_after (this.tab_changed);
}
private void browser_removed (Midori.Browser browser) {
browser.switch_tab.disconnect (this.tab_changed);
}
public void activated (Midori.App app) {
/* FIXME: override behavior without changing the preference */
app.settings.load_on_startup = MidoriStartup.DELAYED_PAGES;
this.preferences_changed ();
Midori.Browser? focused_browser = app.browser;
if (focused_browser == null)
Midori.Timeout.add (50, this.reload_first_tab);
else
this.initialized = true;
foreach (Midori.Browser browser in app.get_browsers ()) {
browser_added (browser);
}
app.add_browser.connect (browser_added);
}
public void deactivated () {
Midori.App app = get_app ();
foreach (Midori.Browser browser in app.get_browsers ()) {
browser_removed (browser);
}
app.add_browser.disconnect (browser_added);
}
internal Manager () {
GLib.Object (name: _("Delayed load"),
description: _("Delay page load until you actually use the tab."),
version: "0.1",
authors: "André Stösel <andre@stoesel.de>");
install_integer ("delay", 0);
activate.connect (this.activated);
deactivate.connect (this.deactivated);
open_preferences.connect (show_preferences);
preferences_changed.connect (preferences_changed_cb);
this.tasks = new HashTable<Midori.Browser, TabShaker> (GLib.direct_hash, GLib.direct_equal);
}
}
}
public Midori.Extension extension_init () {
return new DelayedLoad.Manager ();
}

View file

@ -0,0 +1,347 @@
/*
Copyright (C) 2012 André Stösel <andre@stoesel.de>
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.
*/
using Gtk;
using Soup;
using Katze;
using Midori;
using WebKit;
namespace EDM {
#if !HAVE_WIN32
[DBus (name = "net.launchpad.steadyflow.App")]
interface SteadyflowInterface : GLib.Object {
public abstract void AddFile (string url) throws IOError;
}
#endif
private class DownloadRequest : GLib.Object {
public string uri;
public string auth;
public string referer;
public string? cookie_header;
}
internal Manager manager;
private class Manager : GLib.Object {
private CookieJar cookie_jar;
private GLib.PtrArray download_managers = new GLib.PtrArray ();
public bool download_requested (Midori.View view, WebKit.Download download) {
Midori.DownloadType download_type = download.get_data<Midori.DownloadType> ("midori-download-type");
if (download_type == Midori.DownloadType.SAVE) {
var dlReq = new DownloadRequest ();
dlReq.uri = download.get_uri ();
var request = download.get_network_request ();
var message = request.get_message ();
weak MessageHeaders headers = message.request_headers;
dlReq.auth = headers.get ("Authorization");
dlReq.referer = headers.get ("Referer");
dlReq.cookie_header = this.cookie_jar.get_cookies (new Soup.URI (dlReq.uri), true);
for (var i = 0 ; i < download_managers.len; i++) {
var dm = download_managers.index (i) as ExternalDownloadManager;
if (dm.download (dlReq))
return true;
}
}
return false;
}
public void tab_added (Midori.Browser browser, Midori.View view) {
view.download_requested.connect (download_requested);
}
public void tab_removed (Midori.Browser browser, Midori.View view) {
view.download_requested.disconnect(download_requested);
}
public void browser_added (Midori.Browser browser) {
foreach (var tab in browser.get_tabs ())
tab_added (browser, tab);
browser.add_tab.connect (tab_added);
browser.remove_tab.connect (tab_removed);
}
public void browser_removed (Midori.Browser browser) {
foreach (var tab in browser.get_tabs ())
tab_removed (browser, tab);
browser.add_tab.disconnect (tab_added);
browser.remove_tab.disconnect (tab_removed);
}
public void activated (Midori.Extension extension, Midori.App app) {
this.download_managers.add (extension);
if (this.download_managers.len == 1) {
foreach (var browser in app.get_browsers ())
browser_added (browser);
app.add_browser.connect (browser_added);
}
}
public void deactivated (Midori.Extension extension) {
this.download_managers.remove (extension);
if (this.download_managers.len == 0) {
var app = extension.get_app ();
foreach (var browser in app.get_browsers ())
browser_removed (browser);
app.add_browser.disconnect (browser_added);
}
}
construct {
var session = WebKit.get_default_session ();
this.cookie_jar = session.get_feature (typeof (CookieJar)) as CookieJar;
}
}
private abstract class ExternalDownloadManager : Midori.Extension {
public void activated (Midori.App app) {
manager.activated (this, app);
}
public void deactivated () {
manager.deactivated (this);
}
public void handle_exception (GLib.Error error) {
string ext_name;
this.get ("name",out ext_name);
var dialog = new MessageDialog (null, DialogFlags.MODAL,
MessageType.ERROR, ButtonsType.CLOSE,
_("An error occurred when attempting to download a file with the following plugin:\n" +
"%s\n\n" +
"Error:\n%s\n\n" +
"Carry on without this plugin."
),
ext_name, error.message);
dialog.response.connect ((a) => { dialog.destroy (); });
dialog.run ();
}
public abstract bool download (DownloadRequest dlReq);
}
#if !HAVE_WIN32
private class Aria2 : ExternalDownloadManager {
public override bool download (DownloadRequest dlReq) {
var url = value_array_new ();
value_array_insert (url, 0, typeof (string), dlReq.uri);
GLib.HashTable<string, GLib.Value?> options = value_hash_new ();
var referer = new GLib.Value (typeof (string));
referer.set_string (dlReq.referer);
options.insert ("referer", referer);
var headers = value_array_new ();
if (dlReq.cookie_header != null) {
value_array_insert (headers, 0, typeof (string), "Cookie: %s".printf(dlReq.cookie_header));
}
if (headers.n_values > 0)
options.insert ("header", headers);
var message = XMLRPC.request_new ("http://127.0.0.1:6800/rpc",
"aria2.addUri",
typeof (ValueArray), url,
typeof(HashTable), options);
var session = new SessionSync ();
session.send_message (message);
try {
Value v;
XMLRPC.parse_method_response ((string) message.response_body.flatten ().data, -1, out v);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
internal Aria2 () {
GLib.Object (name: _("External Download Manager - Aria2"),
description: _("Download files with Aria2"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "aria2");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
private class SteadyFlow : ExternalDownloadManager {
public override bool download (DownloadRequest dlReq) {
try {
SteadyflowInterface dm = Bus.get_proxy_sync (
BusType.SESSION,
"net.launchpad.steadyflow.App",
"/net/launchpad/steadyflow/app");
dm.AddFile (dlReq.uri);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
internal SteadyFlow () {
GLib.Object (name: _("External Download Manager - SteadyFlow"),
description: _("Download files with SteadyFlow"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "steadyflow");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
#endif
private class CommandLinePreferences : Gtk.Dialog {
protected Entry input;
protected CommandLine commandline;
public CommandLinePreferences(CommandLine cl) {
this.commandline = cl;
string ext_name;
this.get ("name",out ext_name);
this.title = _("Preferences for %s").printf (ext_name);
if (this.get_class ().find_property ("has-separator") != null)
this.set ("has-separator", false);
this.border_width = 5;
this.set_modal (true);
this.set_default_size (400, 100);
this.create_widgets ();
this.response.connect (response_cb);
}
private void response_cb (Gtk.Dialog source, int response_id) {
switch (response_id) {
case ResponseType.APPLY:
this.commandline.set_string ("commandline", this.input.get_text ());
this.commandline.update_description (this.commandline.get_app ());
this.destroy ();
break;
case ResponseType.CANCEL:
this.destroy ();
break;
}
}
private void create_widgets () {
Label text = new Label (_("Command:"));
this.input = new Entry ();
this.input.set_text (this.commandline.get_string ("commandline"));
#if HAVE_GTK3
Gtk.Box vbox = get_content_area () as Gtk.Box;
vbox.pack_start (text, false, false, 0);
vbox.pack_start (this.input, false, true, 0);
#else
this.vbox.pack_start (text, false, false, 0);
this.vbox.pack_start (this.input, false, true, 0);
#endif
this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);
this.show_all ();
}
}
private class CommandLine : ExternalDownloadManager {
private void show_preferences () {
CommandLinePreferences dialog = new CommandLinePreferences (this);
dialog.show ();
}
public override bool download (DownloadRequest dlReq) {
try {
string cmd = this.get_string ("commandline");
cmd = cmd.replace("{REFERER}", GLib.Shell.quote (dlReq.referer));
if (dlReq.cookie_header != null) {
cmd = cmd.replace("{COOKIES}", GLib.Shell.quote ("Cookie: " + dlReq.cookie_header));
} else {
cmd = cmd.replace("{COOKIES}", "\'\'");
}
cmd = cmd.replace("{URL}", GLib.Shell.quote (dlReq.uri));
GLib.Process.spawn_command_line_async (cmd);
return true;
} catch (Error e) {
this.handle_exception (e);
}
return false;
}
static string description_with_command (string commandline) {
string command;
try {
string[] argvp;
Shell.parse_argv (commandline, out argvp);
command = argvp[0];
}
catch (Error error) {
command = commandline.split (" ")[0];
}
return _("Download files with \"%s\" or a custom command").printf (command);
}
internal void update_description (Midori.App app) {
this.description = description_with_command (get_string ("commandline"));
}
internal CommandLine () {
#if HAVE_WIN32
string default_commandline = "\"%s\\FlashGet\\flashget.exe\" {URL}".printf (Environment.get_variable ("ProgramFiles"));
#elif HAVE_FREEBSD
string default_commandline = "fetch HTTP_REFERER={REFERER} {URL}";
#else
string default_commandline = "wget --no-check-certificate --referer={REFERER} --header={COOKIES} {URL}";
#endif
GLib.Object (name: _("External Download Manager - CommandLine"),
description: description_with_command (default_commandline),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "André Stösel <andre@stoesel.de>",
key: "commandline");
this.install_string ("commandline", default_commandline);
this.activate.connect (activated);
this.activate.connect (update_description);
this.deactivate.connect (deactivated);
this.open_preferences.connect (show_preferences);
}
}
}
public Katze.Array extension_init () {
EDM.manager = new EDM.Manager();
var extensions = new Katze.Array( typeof (Midori.Extension));
#if !HAVE_WIN32
extensions.add_item (new EDM.Aria2 ());
extensions.add_item (new EDM.SteadyFlow ());
#endif
extensions.add_item (new EDM.CommandLine ());
return extensions;
}

View file

@ -95,7 +95,6 @@ atom_get_link (KatzeItem* item,
gboolean newishtml; gboolean newishtml;
gboolean newlink; gboolean newlink;
newlink = FALSE;
oldtype = (gchar*)katze_item_get_meta_string (item, "feedpanel:linktype"); oldtype = (gchar*)katze_item_get_meta_string (item, "feedpanel:linktype");
oldrel = (gchar*)katze_item_get_meta_string (item, "feedpanel:linkrel"); oldrel = (gchar*)katze_item_get_meta_string (item, "feedpanel:linkrel");

View file

@ -24,7 +24,6 @@ struct _FeedPanel
GtkWidget* treeview; GtkWidget* treeview;
GtkWidget* webview; GtkWidget* webview;
GtkWidget* delete; GtkWidget* delete;
GdkPixbuf* pixbuf;
}; };
struct _FeedPanelClass struct _FeedPanelClass
@ -82,22 +81,17 @@ feed_panel_treeview_render_icon_cb (GtkTreeViewColumn* column,
else else
pitem = item; pitem = item;
uri = katze_item_get_uri (pitem); if ((uri = katze_item_get_uri (pitem)))
if (uri)
{ {
pixbuf = katze_load_cached_icon (uri, NULL); if (!(pixbuf = midori_paths_get_icon (uri, NULL)))
if (!pixbuf) pixbuf = gtk_widget_render_icon (panel->treeview, STOCK_NEWS_FEED, GTK_ICON_SIZE_MENU, NULL);
pixbuf = panel->pixbuf;
} }
else else
{ pixbuf = gtk_widget_render_icon (panel->treeview, GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_MENU, NULL);
pixbuf = gtk_widget_render_icon (panel->treeview,
GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_MENU, NULL);
}
g_object_set (renderer, "pixbuf", pixbuf, NULL); g_object_set (renderer, "pixbuf", pixbuf, NULL);
if (pixbuf != panel->pixbuf) if (pixbuf)
g_object_unref (pixbuf); g_object_unref (pixbuf);
} }
@ -326,14 +320,11 @@ feed_panel_row_activated_cb (GtkTreeView* treeview,
uri = katze_item_get_uri (item); uri = katze_item_get_uri (item);
if (uri && *uri) if (uri && *uri)
{ {
MidoriWebSettings* settings; MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
MidoriBrowser* browser; GtkWidget* view = midori_browser_add_item (browser, item);
gint n; MidoriWebSettings* settings = midori_browser_get_settings (browser);
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = midori_browser_get_settings (browser);
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background")) if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n); midori_browser_set_current_tab (browser, view);
} }
g_object_unref (item); g_object_unref (item);
} }
@ -358,49 +349,45 @@ feed_panel_cursor_or_row_changed_cb (GtkTreeView* treeview,
if (KATZE_IS_ARRAY (item)) if (KATZE_IS_ARRAY (item))
{ {
gint64 date;
text = NULL; text = NULL;
if (!uri) if (!uri)
text = g_strdup (katze_item_get_text (KATZE_ITEM (item))); text = g_strdup (katze_item_get_text (KATZE_ITEM (item)));
else else
{ {
KatzeItem* parent; KatzeItem* parent = katze_item_get_parent (item);
const gchar* puri; gint64 added = katze_item_get_added (item);
parent = katze_item_get_parent (item);
g_assert (KATZE_IS_ARRAY (parent)); g_assert (KATZE_IS_ARRAY (parent));
date = katze_item_get_added (item); if (added)
puri = katze_item_get_uri (parent);
if (date)
{ {
time_t date_t; #if GLIB_CHECK_VERSION (2, 26, 0)
const struct tm* tm; GDateTime* date = g_date_time_new_from_unix_local (added);
static gchar date_format[512]; gchar* pretty = g_date_time_format (date, "%c");
gchar* last_updated; g_date_time_unref (date);
#else
static gchar date_fmt[512];
const struct tm *tm = localtime (&added);
/* Some GCC versions falsely complain about "%c" */
strftime (date_fmt, sizeof (date_fmt), "%c", tm);
gchar* pretty = g_strdup (date_fmt);
#endif
date_t = (time_t)date;
tm = localtime (&date_t);
/* Some gcc versions complain about "%c" for no reason */
strftime (date_format, sizeof (date_format), "%c", tm);
/* i18n: The local date a feed was last updated */ /* i18n: The local date a feed was last updated */
last_updated = g_strdup_printf (C_("Feed", "Last updated: %s."), gchar* last_updated = g_strdup_printf (C_("Feed", "Last updated: %s."), pretty);
date_format);
text = g_strdup_printf ( text = g_strdup_printf (
"<html><head><title>feed</title></head>" "<html><head><title>feed</title></head>"
"<body><h3>%s</h3><p />%s</body></html>", "<body><h3>%s</h3><p />%s</body></html>",
puri, last_updated); katze_item_get_uri (KATZE_ITEM (parent)), last_updated);
g_free (pretty);
g_free (last_updated); g_free (last_updated);
} }
else else
{ {
text = g_strdup_printf ( text = g_strdup_printf (
"<html><head><title>feed</title></head>" "<html><head><title>feed</title></head>"
"<body><h3>%s</h3></body></html>", puri); "<body><h3>%s</h3></body></html>", katze_item_get_uri (KATZE_ITEM (parent)));
} }
} }
webkit_web_view_load_html_string ( midori_view_set_html (MIDORI_VIEW (panel->webview), text ? text : "", uri, NULL);
WEBKIT_WEB_VIEW (panel->webview), text ? text : "", uri);
g_free ((gchar*) text); g_free ((gchar*) text);
sensitive = TRUE; sensitive = TRUE;
@ -408,8 +395,7 @@ feed_panel_cursor_or_row_changed_cb (GtkTreeView* treeview,
else else
{ {
text = katze_item_get_text (item); text = katze_item_get_text (item);
webkit_web_view_load_html_string ( midori_view_set_html (MIDORI_VIEW (panel->webview), text ? text : "", uri, NULL);
WEBKIT_WEB_VIEW (panel->webview), text ? text : "", uri);
} }
g_object_unref (item); g_object_unref (item);
} }
@ -468,14 +454,11 @@ feed_panel_open_in_tab_activate_cb (GtkWidget* menuitem,
if ((uri = katze_item_get_uri (item)) && *uri) if ((uri = katze_item_get_uri (item)) && *uri)
{ {
MidoriWebSettings* settings; MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
MidoriBrowser* browser; GtkWidget* view = midori_browser_add_item (browser, item);
MidoriWebSettings* settings = midori_browser_get_settings (browser);
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = midori_browser_get_settings (browser);
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background")) if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n); midori_browser_set_current_tab (browser, view);
} }
} }
@ -563,16 +546,11 @@ feed_panel_button_release_event_cb (GtkWidget* widget,
if (uri && *uri) if (uri && *uri)
{ {
MidoriWebSettings* settings; MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
MidoriBrowser* browser; GtkWidget* view = midori_browser_add_item (browser, item);
gint n; MidoriWebSettings* settings = midori_browser_get_settings (browser);
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = midori_browser_get_settings (browser);
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background")) if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n); midori_browser_set_current_tab (browser, view);
} }
} }
else else
@ -620,6 +598,7 @@ webview_button_press_event_cb (GtkWidget* widget,
return MIDORI_EVENT_CONTEXT_MENU (event); return MIDORI_EVENT_CONTEXT_MENU (event);
} }
#ifndef HAVE_WEBKIT2
static gboolean static gboolean
webview_navigation_request_cb (WebKitWebView* web_view, webview_navigation_request_cb (WebKitWebView* web_view,
WebKitWebFrame* frame, WebKitWebFrame* frame,
@ -631,14 +610,10 @@ webview_navigation_request_cb (WebKitWebView* web_view,
if (webkit_web_navigation_action_get_reason (navigation_action) == if (webkit_web_navigation_action_get_reason (navigation_action) ==
WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
{ {
MidoriBrowser* browser; MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
const gchar* uri; const gchar* uri = webkit_network_request_get_uri (request);
gint n; GtkWidget* view = midori_browser_add_uri (browser, uri);
midori_browser_set_current_tab (browser, view);
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
uri = webkit_network_request_get_uri (request);
n = midori_browser_add_uri (browser, uri);
midori_browser_set_current_page (browser, n);
webkit_web_policy_decision_ignore (policy_decision); webkit_web_policy_decision_ignore (policy_decision);
return TRUE; return TRUE;
@ -646,6 +621,7 @@ webview_navigation_request_cb (WebKitWebView* web_view,
return FALSE; return FALSE;
} }
#endif
static const gchar* static const gchar*
feed_panel_get_label (MidoriViewable* viewable) feed_panel_get_label (MidoriViewable* viewable)
@ -729,9 +705,6 @@ feed_panel_get_toolbar (MidoriViewable* viewable)
static void static void
feed_panel_finalize (GObject* object) feed_panel_finalize (GObject* object)
{ {
FeedPanel* panel = FEED_PANEL (object);
g_object_unref (panel->pixbuf);
} }
static void static void
@ -786,7 +759,7 @@ feed_panel_init (FeedPanel* panel)
GtkIconFactory *factory; GtkIconFactory *factory;
GtkIconSource *icon_source; GtkIconSource *icon_source;
GtkIconSet *icon_set; GtkIconSet *icon_set;
WebKitWebSettings* settings; MidoriWebSettings* settings;
PangoFontDescription* font_desc; PangoFontDescription* font_desc;
const gchar* family; const gchar* family;
gint size; gint size;
@ -838,22 +811,24 @@ feed_panel_init (FeedPanel* panel)
NULL); NULL);
gtk_widget_show (treeview); gtk_widget_show (treeview);
webview = webkit_web_view_new ();
#if GTK_CHECK_VERSION(3,0,0) #if GTK_CHECK_VERSION(3,0,0)
font_desc = gtk_style_context_get_font(gtk_widget_get_style_context(treeview), GTK_STATE_FLAG_NORMAL); font_desc = (PangoFontDescription*)gtk_style_context_get_font (
gtk_widget_get_style_context (treeview), GTK_STATE_FLAG_NORMAL);
#else #else
font_desc = treeview->style->font_desc; font_desc = treeview->style->font_desc;
#endif #endif
family = pango_font_description_get_family (font_desc); family = pango_font_description_get_family (font_desc);
size = pango_font_description_get_size (font_desc) / PANGO_SCALE; size = pango_font_description_get_size (font_desc) / PANGO_SCALE;
settings = webkit_web_settings_new (); settings = midori_web_settings_new ();
g_object_set (settings, "default-font-family", family, g_object_set (settings, "default-font-family", family,
"default-font-size", size, NULL); "default-font-size", size, NULL);
g_object_set (webview, "settings", settings, NULL); webview = midori_view_new_with_item (NULL, settings);
gtk_widget_set_size_request (webview, -1, 50); gtk_widget_set_size_request (webview, -1, 50);
g_object_connect (webview, g_object_connect (midori_tab_get_web_view (MIDORI_TAB (webview)),
#ifndef HAVE_WEBKIT2
"signal::navigation-policy-decision-requested", "signal::navigation-policy-decision-requested",
webview_navigation_request_cb, panel, webview_navigation_request_cb, panel,
#endif
"signal::button-press-event", "signal::button-press-event",
webview_button_press_event_cb, NULL, webview_button_press_event_cb, NULL,
"signal::button-release-event", "signal::button-release-event",
@ -871,13 +846,10 @@ feed_panel_init (FeedPanel* panel)
paned = gtk_vpaned_new (); paned = gtk_vpaned_new ();
gtk_paned_pack1 (GTK_PANED (paned), treewin, TRUE, FALSE); gtk_paned_pack1 (GTK_PANED (paned), treewin, TRUE, FALSE);
gtk_paned_pack2 (GTK_PANED (paned), webview, TRUE, FALSE); gtk_paned_pack2 (GTK_PANED (paned), webview, TRUE, TRUE);
gtk_box_pack_start (GTK_BOX (panel), paned, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (panel), paned, TRUE, TRUE, 0);
gtk_widget_show (webview); gtk_widget_show (webview);
gtk_widget_show (paned); gtk_widget_show (paned);
panel->pixbuf = gtk_widget_render_icon (treeview,
STOCK_NEWS_FEED, GTK_ICON_SIZE_MENU, NULL);
} }
GtkWidget* GtkWidget*

View file

@ -219,6 +219,8 @@ feed_parse_doc (xmlDocPtr doc,
feed_parse_node (fparser); feed_parse_node (fparser);
} }
} }
else
isvalid = FALSE;
fparser->error = NULL; fparser->error = NULL;
fparser->doc = NULL; fparser->doc = NULL;

View file

@ -16,7 +16,6 @@
#include <midori/midori.h> #include <midori/midori.h>
#define EXTENSION_NAME "Feed Panel" #define EXTENSION_NAME "Feed Panel"
#define UPDATE_FREQ 10
#define feed_get_flags(feed) \ #define feed_get_flags(feed) \
GPOINTER_TO_INT (g_object_get_data (G_OBJECT ((feed)), "flags")) GPOINTER_TO_INT (g_object_get_data (G_OBJECT ((feed)), "flags"))
@ -102,23 +101,9 @@ feed_deactivate_cb (MidoriExtension* extension,
} }
} }
static void
feed_dialog_response_cb (GtkWidget* dialog,
gint response,
gpointer data)
{
gtk_widget_destroy (dialog);
}
static KatzeArray* static KatzeArray*
feed_add_item (KatzeArray* feeds, feed_add_item (KatzeArray* feeds,
const gchar* uri) const gchar* uri)
{
KatzeArray* feed;
feed = NULL;
if (uri)
{ {
if (katze_array_find_token (feeds, uri)) if (katze_array_find_token (feeds, uri))
{ {
@ -131,25 +116,24 @@ feed_add_item (KatzeArray* feeds,
_("Feed '%s' already exists"), uri); _("Feed '%s' already exists"), uri);
gtk_window_set_title (GTK_WINDOW (dialog), EXTENSION_NAME); gtk_window_set_title (GTK_WINDOW (dialog), EXTENSION_NAME);
gtk_widget_show (dialog); gtk_widget_show (dialog);
g_signal_connect (dialog, "response", g_signal_connect_swapped (dialog, "response",
G_CALLBACK (feed_dialog_response_cb), NULL); G_CALLBACK (gtk_widget_destroy), dialog);
return NULL;
} }
else else
{ {
KatzeArray* child; KatzeArray* child;
feed = katze_array_new (KATZE_TYPE_ARRAY); KatzeArray* feed = katze_array_new (KATZE_TYPE_ARRAY);
child = katze_array_new (KATZE_TYPE_ITEM); child = katze_array_new (KATZE_TYPE_ITEM);
katze_item_set_uri (KATZE_ITEM (feed), uri); katze_item_set_uri (KATZE_ITEM (feed), uri);
katze_item_set_token (KATZE_ITEM (feed), uri); katze_item_set_token (KATZE_ITEM (feed), uri);
katze_item_set_uri (KATZE_ITEM (child), uri); katze_item_set_uri (KATZE_ITEM (child), uri);
katze_array_add_item (feeds, feed); katze_array_add_item (feeds, feed);
katze_array_add_item (feed, child); katze_array_add_item (feed, child);
}
}
return feed; return feed;
} }
}
static void static void
feed_save_items (MidoriExtension* extension, feed_save_items (MidoriExtension* extension,
@ -464,8 +448,7 @@ feed_app_add_browser_cb (MidoriApp* app,
priv->parsers = g_slist_prepend (priv->parsers, rss_init_parser ()); priv->parsers = g_slist_prepend (priv->parsers, rss_init_parser ());
sfeeds = midori_extension_get_string_list (extension, "feeds", &n); sfeeds = midori_extension_get_string_list (extension, "feeds", &n);
g_assert (n == 0 || sfeeds); if (sfeeds != NULL)
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
if (sfeeds[i]) if (sfeeds[i])
@ -475,7 +458,6 @@ feed_app_add_browser_cb (MidoriApp* app,
update_feed (priv, KATZE_ITEM (feed)); update_feed (priv, KATZE_ITEM (feed));
} }
} }
g_strdupv (sfeeds);
action_group = midori_browser_get_action_group (browser); action_group = midori_browser_get_action_group (browser);
action = gtk_action_group_get_action (action_group, "Location"); action = gtk_action_group_get_action (action_group, "Location");
@ -488,8 +470,8 @@ feed_app_add_browser_cb (MidoriApp* app,
g_signal_connect (extension, "deactivate", g_signal_connect (extension, "deactivate",
G_CALLBACK (feed_deactivate_cb), priv); G_CALLBACK (feed_deactivate_cb), priv);
priv->source_id = g_timeout_add_seconds (UPDATE_FREQ * 60, priv->source_id = midori_timeout_add_seconds (
(GSourceFunc) update_feeds, priv); 600, (GSourceFunc) update_feeds, priv, NULL);
} }
static void static void
@ -512,7 +494,6 @@ MidoriExtension*
extension_init (void) extension_init (void)
{ {
MidoriExtension* extension; MidoriExtension* extension;
gchar* sfeed[2];
extension = g_object_new (MIDORI_TYPE_EXTENSION, extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Feed Panel"), "name", _("Feed Panel"),
@ -521,8 +502,7 @@ extension_init (void)
"authors", "Dale Whittaker <dayul@users.sf.net>", "authors", "Dale Whittaker <dayul@users.sf.net>",
NULL); NULL);
sfeed[0] = NULL; midori_extension_install_string_list (extension, "feeds", NULL, 0);
midori_extension_install_string_list (extension, "feeds", sfeed, 1);
g_signal_connect (extension, "activate", g_signal_connect (extension, "activate",
G_CALLBACK (feed_activate_cb), NULL); G_CALLBACK (feed_activate_cb), NULL);

View file

@ -1,600 +0,0 @@
/*
Copyright (C) 2009 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
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.
*/
#define MAXCHARS 60
#define MINCHARS 2
#include <midori/midori.h>
#include <glib/gstdio.h>
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
static GHashTable* global_keys;
static gchar* jsforms;
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser);
static gboolean
formhistory_prepare_js ()
{
gchar* autosuggest;
gchar* style;
guint i;
gchar* file;
file = sokoke_find_data_filename ("autosuggestcontrol.js", TRUE);
if (!g_file_get_contents (file, &autosuggest, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (autosuggest);
katze_assign (file, sokoke_find_data_filename ("autosuggestcontrol.css", TRUE));
if (!g_file_get_contents (file, &style, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (style);
i = 0;
while (style[i])
{
if (style[i] == '\n')
style[i] = ' ';
i++;
}
jsforms = g_strdup_printf (
"%s"
"window.addEventListener ('DOMContentLoaded',"
"function () {"
" if (document.getElementById('formhistory'))"
" return;"
" if (!initSuggestions ())"
" return;"
" var mystyle = document.createElement('style');"
" mystyle.setAttribute('type', 'text/css');"
" mystyle.setAttribute('id', 'formhistory');"
" mystyle.appendChild(document.createTextNode('%s'));"
" var head = document.getElementsByTagName('head')[0];"
" if (head) head.appendChild(mystyle);"
"}, true);",
autosuggest,
style);
g_strstrip (jsforms);
g_free (file);
g_free (style);
g_free (autosuggest);
return TRUE;
}
static gchar*
formhistory_fixup_value (char* value)
{
guint i = 0;
g_strchomp (value);
while (value[i])
{
if (value[i] == '\n')
value[i] = ' ';
else if (value[i] == '"')
value[i] = '\'';
i++;
}
return value;
}
static gchar*
formhistory_build_js ()
{
GString* suggestions;
GHashTableIter iter;
gpointer key, value;
suggestions = g_string_new (
"function FormSuggestions(eid) { "
"arr = new Array();");
g_hash_table_iter_init (&iter, global_keys);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_string_append_printf (suggestions, " arr[\"%s\"] = [%s]; ",
(gchar*)key, (gchar*)value);
}
g_string_append (suggestions, "this.suggestions = arr[eid]; }");
g_string_append (suggestions, jsforms);
return g_string_free (suggestions, FALSE);
}
static void
formhistory_update_database (gpointer db,
const gchar* key,
const gchar* value)
{
gchar* sqlcmd;
gchar* errmsg;
gint success;
sqlcmd = sqlite3_mprintf ("INSERT INTO forms VALUES"
"('%q', '%q', '%q')",
NULL, key, value);
success = sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
if (success != SQLITE_OK)
{
g_printerr (_("Failed to add form value: %s\n"), errmsg);
g_free (errmsg);
return;
}
}
static gboolean
formhistory_update_main_hash (gchar* key,
gchar* value)
{
guint length;
gchar* tmp;
if (!(value && *value))
return FALSE;
length = strlen (value);
if (length > MAXCHARS || length < MINCHARS)
return FALSE;
formhistory_fixup_value (key);
formhistory_fixup_value (value);
if ((tmp = g_hash_table_lookup (global_keys, (gpointer)key)))
{
gchar* rvalue = g_strdup_printf ("\"%s\"",value);
gchar* patt = g_regex_escape_string (rvalue, -1);
if (!g_regex_match_simple (patt, tmp,
G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
{
gchar* new_value = g_strdup_printf ("%s%s,", tmp, rvalue);
g_hash_table_insert (global_keys, g_strdup (key), new_value);
g_free (rvalue);
g_free (patt);
}
else
{
g_free (rvalue);
g_free (patt);
return FALSE;
}
}
else
{
gchar* new_value = g_strdup_printf ("\"%s\",",value);
g_hash_table_replace (global_keys, g_strdup (key), new_value);
}
return TRUE;
}
static gboolean
formhistory_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriExtension* extension)
{
/* The script returns form data in the form "field_name|,|value|,|field_type".
We are handling only input fields with 'text' or 'password' type.
The field separator is "|||" */
const gchar* script = "function dumpForm (inputs) {"
" var out = '';"
" for (i=0;i<inputs.length;i++) {"
" if (inputs[i].getAttribute('autocomplete') == 'off')"
" continue;"
" if (inputs[i].value && (inputs[i].type == 'text' || inputs[i].type == 'password')) {"
" var ename = inputs[i].getAttribute('name');"
" var eid = inputs[i].getAttribute('id');"
" if (!ename && eid)"
" ename=eid;"
" if (inputs[i].getAttribute('autocomplete') != 'off')"
" out += ename+'|,|'+inputs[i].value +'|,|'+inputs[i].type +'|||';"
" }"
" }"
" return out;"
"}"
"dumpForm (document.getElementsByTagName('input'))";
if (webkit_web_navigation_action_get_reason (action) == WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED)
{
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
gchar* value = sokoke_js_script_eval (js_context, script, NULL);
if (value && *value)
{
gpointer db = g_object_get_data (G_OBJECT (extension), "formhistory-db");
gchar** inputs = g_strsplit (value, "|||", 0);
guint i = 0;
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
/* FIXME: We need to handle passwords */
if (strcmp (parts[2], "password"))
{
if (formhistory_update_main_hash (parts[0], parts[1]))
formhistory_update_database (db, parts[0], parts[1]);
}
}
g_strfreev (parts);
i++;
}
g_strfreev (inputs);
g_free (value);
}
}
return FALSE;
}
static void
formhistory_window_object_cleared_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
JSContextRef js_context,
JSObjectRef js_window)
{
gchar* script;
const gchar* page_uri;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!midori_uri_is_http (page_uri))
return;
script = formhistory_build_js ();
sokoke_js_script_eval (js_context, script, NULL);
g_free (script);
}
static void
formhistory_add_tab_cb (MidoriBrowser* browser,
MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_connect (web_view, "window-object-cleared",
G_CALLBACK (formhistory_window_object_cleared_cb), NULL);
g_signal_connect (web_view, "navigation-policy-decision-requested",
G_CALLBACK (formhistory_navigation_decision_cb), extension);
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser);
static void
formhistory_add_tab_foreach_cb (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
formhistory_add_tab_cb (browser, view, extension);
}
static void
formhistory_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkAccelGroup* acg = gtk_accel_group_new ();
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action = gtk_action_new ("FormHistoryToggleState",
_("Toggle form history state"),
_("Activate or deactivate form history for the current tab."), NULL);
gtk_window_add_accel_group (GTK_WINDOW (browser), acg);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", extension);
g_signal_connect (action, "activate",
G_CALLBACK (formhistory_toggle_state_cb), browser);
gtk_action_group_add_action_with_accel (action_group, action, "<Ctrl><Shift>F");
gtk_action_set_accel_group (action, acg);
gtk_action_connect_accelerator (action);
if (midori_extension_get_boolean (extension, "always-load"))
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
g_signal_connect (extension, "deactivate",
G_CALLBACK (formhistory_deactivate_cb), browser);
}
static void
formhistory_deactivate_tabs (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_window_object_cleared_cb, NULL);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_navigation_decision_cb, extension);
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser)
{
MidoriApp* app = midori_extension_get_app (extension);
sqlite3* db;
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action;
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
g_signal_handlers_disconnect_by_func (
extension, formhistory_deactivate_cb, browser);
g_signal_handlers_disconnect_by_func (
app, formhistory_app_add_browser_cb, extension);
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tabs, extension);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", NULL);
action = gtk_action_group_get_action ( action_group, "FormHistoryToggleState");
if (action != NULL)
{
gtk_action_group_remove_action (action_group, action);
g_object_unref (action);
}
katze_assign (jsforms, NULL);
if (global_keys)
g_hash_table_destroy (global_keys);
if ((db = g_object_get_data (G_OBJECT (extension), "formhistory-db")))
sqlite3_close (db);
}
static int
formhistory_add_field (gpointer data,
int argc,
char** argv,
char** colname)
{
gint i;
gint ncols = 3;
/* Test whether have the right number of columns */
g_return_val_if_fail (argc % ncols == 0, 1);
for (i = 0; i < (argc - ncols) + 1; i++)
{
if (argv[i])
{
if (colname[i] && !g_ascii_strcasecmp (colname[i], "domain")
&& colname[i + 1] && !g_ascii_strcasecmp (colname[i + 1], "field")
&& colname[i + 2] && !g_ascii_strcasecmp (colname[i + 2], "value"))
{
gchar* key = argv[i + 1];
formhistory_update_main_hash (g_strdup (key), g_strdup (argv[i + 2]));
}
}
}
return 0;
}
static void
formhistory_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
const gchar* config_dir;
gchar* filename;
sqlite3* db;
char* errmsg = NULL, *errmsg2 = NULL;
KatzeArray* browsers;
MidoriBrowser* browser;
global_keys = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)g_free);
if(!jsforms)
formhistory_prepare_js ();
config_dir = midori_extension_get_config_dir (extension);
katze_mkdir_with_parents (config_dir, 0700);
filename = g_build_filename (config_dir, "forms.db", NULL);
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
/* If the folder is /, this is a test run, thus no error */
if (!g_str_equal (midori_extension_get_config_dir (extension), "/"))
g_warning (_("Failed to open database: %s\n"), sqlite3_errmsg (db));
sqlite3_close (db);
}
g_free (filename);
if ((sqlite3_exec (db, "CREATE TABLE IF NOT EXISTS "
"forms (domain text, field text, value text)",
NULL, NULL, &errmsg) == SQLITE_OK)
&& (sqlite3_exec (db, "SELECT domain, field, value FROM forms ",
formhistory_add_field,
NULL, &errmsg2) == SQLITE_OK))
g_object_set_data (G_OBJECT (extension), "formhistory-db", db);
else
{
if (errmsg)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg);
sqlite3_free (errmsg);
if (errmsg2)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg2);
sqlite3_free (errmsg2);
}
}
sqlite3_close (db);
}
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
formhistory_app_add_browser_cb (app, browser, extension);
g_signal_connect (app, "add-browser",
G_CALLBACK (formhistory_app_add_browser_cb), extension);
g_object_unref (browsers);
}
static void
formhistory_preferences_response_cb (GtkWidget* dialog,
gint response_id,
MidoriExtension* extension)
{
GtkWidget* checkbox;
gboolean old_state;
gboolean new_state;
MidoriApp* app;
KatzeArray* browsers;
MidoriBrowser* browser;
if (response_id == GTK_RESPONSE_APPLY)
{
checkbox = g_object_get_data (G_OBJECT (dialog), "always-load-checkbox");
new_state = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
old_state = midori_extension_get_boolean (extension, "always-load");
if (old_state != new_state)
{
midori_extension_set_boolean (extension, "always-load", new_state);
app = midori_extension_get_app (extension);
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tabs, extension);
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
if (new_state)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
}
}
}
gtk_widget_destroy (dialog);
}
static void
formhistory_preferences_cb (MidoriExtension* extension)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* checkbox;
dialog = gtk_dialog_new ();
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
checkbox = gtk_check_button_new_with_label (_("only activate form history via hotkey (Ctrl+Shift+F) per tab"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
!midori_extension_get_boolean (extension, "always-load"));
g_object_set_data (G_OBJECT (dialog), "always-load-checkbox", checkbox);
gtk_container_add (GTK_CONTAINER (content_area), checkbox);
g_signal_connect (dialog,
"response",
G_CALLBACK (formhistory_preferences_response_cb),
extension);
gtk_widget_show_all (dialog);
}
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser)
{
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
MidoriExtension* extension = g_object_get_data (G_OBJECT (browser), "FormHistoryExtension");
GtkWidget* web_view = midori_view_get_web_view (view);
if (g_signal_handler_find (web_view, G_SIGNAL_MATCH_FUNC,
g_signal_lookup ("window-object-cleared", MIDORI_TYPE_VIEW), 0, NULL,
formhistory_window_object_cleared_cb, extension))
{
formhistory_deactivate_tabs (view, browser, extension);
} else {
formhistory_add_tab_cb (browser, view, extension);
}
}
#if G_ENABLE_DEBUG
/*
<html>
<head>
<title>autosuggest testcase</title>
</head>
<body>
<form method=post>
<p><input type="text" id="txt1" /></p>
<p><input type="text" name="txt2" /></p>
<input type=submit>
</form>
</body>
</html> */
#endif
MidoriExtension*
extension_init (void)
{
gboolean should_init = TRUE;
const gchar* ver;
gchar* desc;
MidoriExtension* extension;
if (formhistory_prepare_js ())
{
ver = "1.0" MIDORI_VERSION_SUFFIX;
desc = g_strdup (_("Stores history of entered form data"));
}
else
{
desc = g_strdup_printf (_("Not available: %s"),
_("Resource files not installed"));
ver = NULL;
should_init = FALSE;
}
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Form history filler"),
"description", desc,
"version", ver,
"authors", "Alexander V. Butenko <a.butenka@gmail.com>",
NULL);
g_free (desc);
if (should_init)
{
midori_extension_install_boolean (extension, "always-load", TRUE);
g_signal_connect (extension, "activate",
G_CALLBACK (formhistory_activate_cb), NULL);
g_signal_connect (extension, "open-preferences",
G_CALLBACK (formhistory_preferences_cb), NULL);
}
return extension;
}

View file

@ -0,0 +1,74 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#ifndef __FORMHISTORY_FRONTEND_H__
#define __FORMHISTORY_FRONTEND_H__
#include <midori/midori.h>
#include <glib/gstdio.h>
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if WEBKIT_CHECK_VERSION (1, 3, 1)
#define FORMHISTORY_USE_GDOM 1
#else
#define FORMHISTORY_USE_JS 1
#endif
#define MAXPASSSIZE 64
typedef struct
{
sqlite3* db;
#ifdef FORMHISTORY_USE_GDOM
WebKitDOMElement* element;
int completion_timeout;
GtkTreeModel* completion_model;
GtkWidget* treeview;
GtkWidget* popup;
gchar* oldkeyword;
glong selection_index;
#else
gchar* jsforms;
#endif
gchar* master_password;
int master_password_canceled;
} FormHistoryPriv;
typedef struct
{
gchar* domain;
gchar* form_data;
FormHistoryPriv* priv;
} FormhistoryPasswordEntry;
FormHistoryPriv*
formhistory_private_new ();
void
formhistory_private_destroy (FormHistoryPriv *priv);
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv);
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension);
#ifdef FORMHISTORY_USE_GDOM
void
formhistory_suggestions_hide_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv);
#endif
#endif

View file

@ -0,0 +1,514 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#include "formhistory-frontend.h"
#ifdef FORMHISTORY_USE_GDOM
#define COMPLETION_DELAY 200
FormHistoryPriv*
formhistory_private_new ()
{
FormHistoryPriv* priv = g_slice_new (FormHistoryPriv);
priv->oldkeyword = g_strdup ("");
priv->selection_index = -1;
return priv;
}
void
formhistory_suggestions_hide_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
if (gtk_widget_get_visible (priv->popup))
gtk_widget_hide (priv->popup);
priv->selection_index = -1;
}
static void
formhistory_suggestion_set (GtkTreePath* path,
FormHistoryPriv* priv)
{
GtkTreeIter iter;
gchar* value;
if (!gtk_tree_model_get_iter (priv->completion_model, &iter, path))
return;
gtk_tree_model_get (priv->completion_model, &iter, 0, &value, -1);
g_object_set (priv->element, "value", value, NULL);
g_free (value);
}
static gboolean
formhistory_suggestion_selected_cb (GtkWidget* treeview,
GdkEventButton* event,
FormHistoryPriv* priv)
{
GtkTreePath* path;
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
event->x, event->y, &path, NULL, NULL, NULL))
{
formhistory_suggestion_set (path, priv);
formhistory_suggestions_hide_cb (NULL, NULL, priv);
gtk_tree_path_free (path);
return TRUE;
}
return FALSE;
}
static void
formhistory_suggestion_remove (GtkTreePath* path,
FormHistoryPriv* priv)
{
GtkTreeIter iter;
gchar* sqlcmd;
char* errmsg = NULL;
gchar* name;
gchar* value;
if (!gtk_tree_model_get_iter (priv->completion_model, &iter, path))
return;
if (!priv->db)
return;
gtk_tree_model_get (priv->completion_model, &iter, 0, &value, -1);
g_object_get (priv->element, "name", &name, NULL);
gtk_list_store_remove (GTK_LIST_STORE (priv->completion_model), &iter);
sqlcmd = sqlite3_mprintf ("DELETE FROM forms WHERE field = '%q' AND value = '%q'",
name, value);
g_free (name);
g_free (value);
sqlite3_exec (priv->db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
}
static void
get_absolute_offset_for_element (WebKitDOMElement* element,
WebKitDOMDocument* element_document,
WebKitDOMNodeList* frames,
glong* x,
glong* y,
gboolean ismainframe)
{
WebKitDOMElement* offset_parent;
gint offset_top = 0, offset_left = 0;
gulong i;
g_object_get (element, "offset-left", &offset_left,
"offset-top", &offset_top,
"offset-parent", &offset_parent,
NULL);
*x += offset_left;
*y += offset_top;
/* To avoid deadlock check only first element of the mainframe parent */
if (ismainframe == TRUE)
return;
if (offset_parent)
goto finish;
/* Element havent returned any parents. Thats mean or there is no parents or we are inside the frame
Loop over all frames we have to find frame == element_document which is a root for our element
and get its offsets */
for (i = 0; i < webkit_dom_node_list_get_length (frames); i++)
{
WebKitDOMDocument *fdoc;
WebKitDOMNode *frame = webkit_dom_node_list_item (frames, i);
if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (frame))
fdoc = webkit_dom_html_iframe_element_get_content_document (WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame));
else
fdoc = webkit_dom_html_frame_element_get_content_document (WEBKIT_DOM_HTML_FRAME_ELEMENT (frame));
if (fdoc == element_document)
{
offset_parent = WEBKIT_DOM_ELEMENT (frame);
ismainframe = TRUE;
/* Add extra 4px to ~cover size of borders */
*y += 4;
break;
}
}
finish:
if (offset_parent)
get_absolute_offset_for_element (offset_parent, element_document, frames, x, y, ismainframe);
}
static void
formhistory_reposition_popup (FormHistoryPriv* priv)
{
WebKitDOMDocument* element_document;
WebKitDOMNodeList* frames;
GtkWidget* view;
GdkWindow* window;
GtkWidget* toplevel;
gint rx, ry;
gint wx, wy;
glong x = 0, y = 0;
glong height;
view = g_object_get_data (G_OBJECT (priv->element), "webview");
toplevel = gtk_widget_get_toplevel (view);
/* Position of a root window */
window = gtk_widget_get_window (toplevel);
gdk_window_get_position (window, &rx, &ry);
/* Postion of webview in root window */
window = gtk_widget_get_window (view);
gdk_window_get_position (window, &wx, &wy);
/* Position of editbox on the webview */
frames = g_object_get_data (G_OBJECT (priv->element), "framelist");
element_document = g_object_get_data (G_OBJECT (priv->element), "doc");
get_absolute_offset_for_element (priv->element, element_document, frames, &x, &y, FALSE);
/* Add height as menu should start under editbox, now on top of it */
g_object_get (priv->element, "client-height", &height, NULL);
y += height + 1;
gtk_window_move (GTK_WINDOW (priv->popup), rx + wx + x, ry +wy + y);
/* Window configuration */
gtk_window_set_screen (GTK_WINDOW (priv->popup), gtk_widget_get_screen (view));
gtk_window_set_transient_for (GTK_WINDOW (priv->popup), GTK_WINDOW (toplevel));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->treeview));
/* FIXME: Adjust size according to treeview width and some reasonable height */
gtk_window_resize (GTK_WINDOW (priv->popup), 50, 80);
}
static gboolean
formhistory_suggestions_show (FormHistoryPriv* priv)
{
GtkListStore* store;
static sqlite3_stmt* stmt;
gchar* value, * name;
const char* sqlcmd;
gint result;
gchar* likedvalue;
int pos = 0;
g_return_val_if_fail (priv->element, FALSE);
g_object_get (priv->element,
"name", &name,
"value", &value,
NULL);
katze_assign (priv->oldkeyword, g_strdup (value));
if (!priv->popup)
formhistory_construct_popup_gui (priv);
if (!stmt)
{
if (!priv->db)
goto free_data;
sqlcmd = "SELECT DISTINCT value FROM forms WHERE field = ?1 and value like ?2";
sqlite3_prepare_v2 (priv->db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
likedvalue = g_strdup_printf ("%s%%", value);
sqlite3_bind_text (stmt, 1, name, -1, NULL);
sqlite3_bind_text (stmt, 2, likedvalue, -1, g_free);
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to select suggestions\n"));
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
formhistory_suggestions_hide_cb (NULL, NULL, priv);
goto free_data;
}
store = GTK_LIST_STORE (priv->completion_model);
gtk_list_store_clear (store);
while (result == SQLITE_ROW)
{
const unsigned char* text = sqlite3_column_text (stmt, 0);
pos++;
gtk_list_store_insert_with_values (store, NULL, pos, 0, text, -1);
result = sqlite3_step (stmt);
}
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
if (!gtk_widget_get_visible (priv->popup))
{
formhistory_reposition_popup (priv);
gtk_widget_show_all (priv->popup);
}
free_data:
g_free (name);
g_free (value);
return FALSE;
}
static void
formhistory_editbox_key_pressed_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
glong key;
GtkTreePath* path;
gchar* keyword;
gint matches;
/* FIXME: Priv is still set after module is disabled */
g_return_if_fail (priv);
g_return_if_fail (element);
if (priv->completion_timeout > 0)
g_source_remove (priv->completion_timeout);
katze_object_assign (priv->element, g_object_ref (element));
key = webkit_dom_ui_event_get_key_code (WEBKIT_DOM_UI_EVENT (dom_event));
switch (key)
{
/* ESC key*/
case 27:
case 35:
case 36:
/* Left key*/
case 37:
/* Right key*/
case 39:
/* Enter key*/
case 13:
if (key == 27)
g_object_set (element, "value", priv->oldkeyword, NULL);
formhistory_suggestions_hide_cb (element, dom_event, priv);
return;
break;
/* Del key */
case 46:
/* Up key */
case 38:
/* Down key */
case 40:
if (!gtk_widget_get_visible (priv->popup))
{
formhistory_suggestions_show (priv);
return;
}
matches = gtk_tree_model_iter_n_children (priv->completion_model, NULL);
if (key == 38)
{
if (priv->selection_index <= 0)
priv->selection_index = matches - 1;
else
priv->selection_index = MAX (priv->selection_index - 1, 0);
}
else if (key == 40)
{
if (priv->selection_index == matches - 1)
priv->selection_index = 0;
else
priv->selection_index = MIN (priv->selection_index + 1, matches -1);
}
if (priv->selection_index == -1)
{
/* No element is selected */
return;
}
path = gtk_tree_path_new_from_indices (priv->selection_index, -1);
if (key == 46)
{
g_object_set (element, "value", priv->oldkeyword, NULL);
formhistory_suggestion_remove (path, priv);
matches--;
}
if (matches == 0)
formhistory_suggestions_hide_cb (element, dom_event, priv);
else
{
gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->treeview), path, NULL, FALSE);
formhistory_suggestion_set (path, priv);
}
gtk_tree_path_free (path);
return;
break;
/* PgUp, PgDn, Ins */
case 33:
case 34:
case 45:
/* Shift, Ctrl, Alt, Tab, Caps Lock*/
case 16:
case 17:
case 18:
case 20:
case 9:
return;
break;
}
g_object_get (element, "value", &keyword, NULL);
if (!(keyword && *keyword && *keyword != ' '))
{
formhistory_suggestions_hide_cb (element, dom_event, priv);
goto free_data;
}
/* If the same keyword is submitted there's no need to regenerate suggestions */
if (gtk_widget_get_visible (priv->popup) &&
!g_strcmp0 (keyword, priv->oldkeyword))
goto free_data;
priv->completion_timeout = midori_timeout_add (COMPLETION_DELAY,
(GSourceFunc)formhistory_suggestions_show, priv, NULL);
free_data:
g_free (keyword);
}
static void
formhistory_DOMContentLoaded_cb (WebKitDOMElement* window,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
gulong i;
WebKitDOMDocument* doc;
WebKitDOMNodeList* inputs;
WebKitDOMNodeList* frames;
GtkWidget* web_view;
if (WEBKIT_DOM_IS_DOCUMENT (window))
doc = WEBKIT_DOM_DOCUMENT (window);
else
doc = webkit_dom_dom_window_get_document (WEBKIT_DOM_DOM_WINDOW (window));
inputs = webkit_dom_document_query_selector_all (doc, "input[type='text']", NULL);
frames = g_object_get_data (G_OBJECT (window), "framelist");
web_view = g_object_get_data (G_OBJECT (window), "webview");
for (i = 0; i < webkit_dom_node_list_get_length (inputs); i++)
{
WebKitDOMNode* element = webkit_dom_node_list_item (inputs, i);
#if WEBKIT_CHECK_VERSION (1, 6, 1)
gchar* autocomplete = webkit_dom_html_input_element_get_autocomplete (
WEBKIT_DOM_HTML_INPUT_ELEMENT (element));
gboolean off = !g_strcmp0 (autocomplete, "off");
g_free (autocomplete);
if (off)
continue;
#endif
g_object_set_data (G_OBJECT (element), "doc", doc);
g_object_set_data (G_OBJECT (element), "webview", web_view);
g_object_set_data (G_OBJECT (element), "framelist", frames);
/* Add dblclick? */
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (element), "keyup",
G_CALLBACK (formhistory_editbox_key_pressed_cb), false,
priv);
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (element), "blur",
G_CALLBACK (formhistory_suggestions_hide_cb), false,
priv);
}
}
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension)
{
WebKitDOMDocument* doc;
WebKitDOMNodeList* frames;
gulong i;
FormHistoryPriv* priv = g_object_get_data (G_OBJECT (extension), "priv");
doc = webkit_web_view_get_dom_document (web_view);
frames = webkit_dom_document_query_selector_all (doc, "iframe, frame", NULL);
g_object_set_data (G_OBJECT (doc), "framelist", frames);
g_object_set_data (G_OBJECT (doc), "webview", web_view);
/* Connect to DOMContentLoaded of the main frame */
webkit_dom_event_target_add_event_listener(
WEBKIT_DOM_EVENT_TARGET (doc), "DOMContentLoaded",
G_CALLBACK (formhistory_DOMContentLoaded_cb), false,
priv);
/* Connect to DOMContentLoaded of frames */
for (i = 0; i < webkit_dom_node_list_get_length (frames); i++)
{
WebKitDOMDOMWindow* framewin;
WebKitDOMNode* frame = webkit_dom_node_list_item (frames, i);
if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (frame))
framewin = webkit_dom_html_iframe_element_get_content_window (WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame));
else
framewin = webkit_dom_html_frame_element_get_content_window (WEBKIT_DOM_HTML_FRAME_ELEMENT (frame));
g_object_set_data (G_OBJECT (framewin), "framelist", frames);
g_object_set_data (G_OBJECT (framewin), "webview", (GtkWidget*)web_view);
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (framewin), "DOMContentLoaded",
G_CALLBACK (formhistory_DOMContentLoaded_cb), false,
priv);
}
formhistory_suggestions_hide_cb (NULL, NULL, priv);
}
void
formhistory_private_destroy (FormHistoryPriv *priv)
{
if (priv->db)
{
sqlite3_close (priv->db);
priv->db = NULL;
}
katze_assign (priv->oldkeyword, NULL);
gtk_widget_destroy (priv->popup);
priv->popup = NULL;
katze_object_assign (priv->element, NULL);
g_slice_free (FormHistoryPriv, priv);
}
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv)
{
GtkTreeModel* model = NULL;
GtkWidget* popup;
GtkWidget* popup_frame;
GtkWidget* scrolled;
GtkWidget* treeview;
GtkCellRenderer* renderer;
GtkTreeViewColumn* column;
model = (GtkTreeModel*) gtk_list_store_new (1, G_TYPE_STRING);
priv->completion_model = model;
popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_COMBO);
popup_frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (popup_frame), GTK_SHADOW_ETCHED_IN);
gtk_container_add (GTK_CONTAINER (popup), popup_frame);
scrolled = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
"hscrollbar-policy", GTK_POLICY_NEVER,
"vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL);
gtk_container_add (GTK_CONTAINER (popup_frame), scrolled);
treeview = gtk_tree_view_new_with_model (model);
priv->treeview = treeview;
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (treeview), TRUE);
gtk_container_add (GTK_CONTAINER (scrolled), treeview);
gtk_widget_set_size_request (gtk_scrolled_window_get_vscrollbar (
GTK_SCROLLED_WINDOW (scrolled)), -1, 0);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("suggestions", renderer, "text", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
priv->popup = popup;
g_signal_connect (treeview, "button-press-event",
G_CALLBACK (formhistory_suggestion_selected_cb), priv);
return TRUE;
}
#endif

View file

@ -0,0 +1,141 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#include "formhistory-frontend.h"
#ifdef FORMHISTORY_USE_JS
FormHistoryPriv*
formhistory_private_new ()
{
FormHistoryPriv* priv = g_slice_new (FormHistoryPriv);
return priv;
}
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv)
{
gchar* autosuggest;
gchar* style;
guint i;
gchar* file;
file = midori_app_find_res_filename ("autosuggestcontrol.js");
if (!g_file_get_contents (file, &autosuggest, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (autosuggest);
katze_assign (file, midori_app_find_res_filename ("autosuggestcontrol.css"));
if (!g_file_get_contents (file, &style, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (style);
g_free (file);
i = 0;
while (style[i])
{
if (style[i] == '\n')
style[i] = ' ';
i++;
}
priv->jsforms = g_strdup_printf (
"%s"
"window.addEventListener ('DOMContentLoaded',"
"function () {"
" if (document.getElementById('formhistory'))"
" return;"
" if (!initSuggestions ())"
" return;"
" var mystyle = document.createElement('style');"
" mystyle.setAttribute('type', 'text/css');"
" mystyle.setAttribute('id', 'formhistory');"
" mystyle.appendChild(document.createTextNode('%s'));"
" var head = document.getElementsByTagName('head')[0];"
" if (head) head.appendChild(mystyle);"
"}, true);",
autosuggest,
style);
g_strstrip (priv->jsforms);
g_free (style);
g_free (autosuggest);
return TRUE;
}
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension)
{
GString* suggestions;
FormHistoryPriv* priv;
static sqlite3_stmt* stmt;
const char* sqlcmd;
const unsigned char* key;
const unsigned char* value;
gint result, pos;
priv = g_object_get_data (G_OBJECT (extension), "priv");
if (!priv->db)
return;
if (!stmt)
{
sqlcmd = "SELECT DISTINCT group_concat(value,'\",\"'), field FROM forms \
GROUP BY field ORDER BY field";
sqlite3_prepare_v2 (priv->db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to select suggestions\n"));
sqlite3_reset (stmt);
return;
}
suggestions = g_string_new (
"function FormSuggestions(eid) { "
"arr = new Array();");
while (result == SQLITE_ROW)
{
pos++;
value = sqlite3_column_text (stmt, 0);
key = sqlite3_column_text (stmt, 1);
if (value)
{
g_string_append_printf (suggestions, " arr[\"%s\"] = [\"%s\"]; ",
(gchar*)key, (gchar*)value);
}
result = sqlite3_step (stmt);
}
g_string_append (suggestions, "this.suggestions = arr[eid]; }");
g_string_append (suggestions, priv->jsforms);
sokoke_js_script_eval (js_context, suggestions->str, NULL);
g_string_free (suggestions, TRUE);
}
void
formhistory_private_destroy (FormHistoryPriv *priv)
{
if (priv->db)
{
sqlite3_close (priv->db);
priv->db = NULL;
}
katze_assign (priv->jsforms, NULL);
g_slice_free (FormHistoryPriv, priv);
}
#endif

View file

@ -0,0 +1,694 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#define MAXCHARS 60
#define MINCHARS 2
#define GTK_RESPONSE_IGNORE 99
#include "formhistory-frontend.h"
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser);
static void
formhistory_update_database (gpointer db,
const gchar* host,
const gchar* key,
const gchar* value)
{
gchar* sqlcmd;
gchar* errmsg;
gint success;
if (!(value && *value))
return;
sqlcmd = sqlite3_mprintf ("INSERT INTO forms VALUES('%q', '%q', '%q')",
host, key, value);
success = sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
if (success != SQLITE_OK)
{
g_printerr (_("Failed to add form value: %s\n"), errmsg);
g_free (errmsg);
return;
}
}
static gchar*
formhistory_get_login_data (gpointer db,
const gchar* uri)
{
gchar* domain;
static sqlite3_stmt* stmt;
gint result;
gchar* value = NULL;
g_return_val_if_fail (db != NULL, NULL);
g_return_val_if_fail (uri != NULL, NULL);
domain = midori_uri_parse_hostname (uri, NULL);
g_return_val_if_fail (domain != NULL, NULL);
if (!stmt)
{
const gchar* sqlcmd = "SELECT value FROM forms WHERE domain = ?1 and field = 'MidoriPasswordManager' limit 1";
sqlite3_prepare_v2 (db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
sqlite3_bind_text (stmt, 1, domain, -1, NULL);
result = sqlite3_step (stmt);
if (result == SQLITE_ROW)
value = g_strdup ((gchar*)sqlite3_column_text (stmt, 0));
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
g_free (domain);
return value;
}
static gboolean
formhistory_check_master_password (GtkWidget* parent,
FormHistoryPriv* priv)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* hbox;
GtkWidget* image;
GtkWidget* label;
GtkWidget* entry;
const gchar* title;
static int alive;
gboolean ret = FALSE;
/* Password is set */
if (priv->master_password && *priv->master_password)
return TRUE;
/* Other prompt is active */
if (alive == 1)
return FALSE;
/* Prompt was cancelled */
if (priv->master_password_canceled == 1)
return FALSE;
alive = 1;
title = _("Form history");
dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_DIALOG_AUTHENTICATION);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_container_set_border_width (GTK_CONTAINER (content_area), 5);
hbox = gtk_hbox_new (FALSE, 8);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
label = gtk_label_new (_("Master password required\n"
"to open password database"));
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
entry = gtk_entry_new ();
g_object_set (entry, "truncate-multiline", TRUE, NULL);
gtk_entry_set_visibility(GTK_ENTRY (entry),FALSE);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_container_add (GTK_CONTAINER (content_area), entry);
gtk_widget_show_all (entry);
gtk_widget_show_all (hbox);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
/* FIXME: add password verification */
katze_assign (priv->master_password,
g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))));
ret = TRUE;
}
else
priv->master_password_canceled = 1;
gtk_widget_destroy (dialog);
alive = 0;
return ret;
}
static gchar*
formhistory_encrypt (const gchar* data,
const gchar* password)
{
/* TODO: Implement persistent storage/ keyring support */
return NULL;
}
static void
formhistory_remember_password_response (GtkWidget* infobar,
gint response_id,
FormhistoryPasswordEntry* entry)
{
gchar* encrypted_form;
if (response_id == GTK_RESPONSE_IGNORE)
goto cleanup;
if (formhistory_check_master_password (NULL, entry->priv))
{
if (response_id != GTK_RESPONSE_ACCEPT)
katze_assign (entry->form_data, g_strdup ("never"));
if ((encrypted_form = formhistory_encrypt (entry->form_data,
entry->priv->master_password)))
formhistory_update_database (entry->priv->db, entry->domain, "MidoriPasswordManager", encrypted_form);
g_free (encrypted_form);
}
cleanup:
g_free (entry->form_data);
g_free (entry->domain);
g_slice_free (FormhistoryPasswordEntry, entry);
gtk_widget_destroy (infobar);
}
static gboolean
formhistory_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriExtension* extension)
{
FormHistoryPriv* priv;
JSContextRef js_context;
gchar* value;
/* The script returns form data in the form "field_name|,|value|,|field_type".
We are handling only input fields with 'text' or 'password' type.
The field separator is "|||" */
const gchar* script = "function dumpForm (inputs) {"
" var out = '';"
" for (var i = 0; i < inputs.length; i++) {"
" if (inputs[i].getAttribute('autocomplete') == 'off' && "
" inputs[i].type == 'text')"
" continue;"
" if (inputs[i].value && (inputs[i].type == 'text' || inputs[i].type == 'password')) {"
" var ename = inputs[i].getAttribute('name');"
" var eid = inputs[i].getAttribute('id');"
" if (!eid && ename)"
" eid=ename;"
" out += eid+'|,|'+inputs[i].value +'|,|'+inputs[i].type +'|||';"
" }"
" }"
" return out;"
"}"
"dumpForm (document.getElementsByTagName('input'))";
if (webkit_web_navigation_action_get_reason (action) != WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED)
return FALSE;
priv = g_object_get_data (G_OBJECT (extension), "priv");
js_context = webkit_web_frame_get_global_context (web_frame);
value = sokoke_js_script_eval (js_context, script, NULL);
#ifdef FORMHISTORY_USE_GDOM
formhistory_suggestions_hide_cb (NULL, NULL, priv);
#endif
if (value && *value)
{
gchar** inputs = g_strsplit (value, "|||", 0);
guint i = 0;
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
if (strcmp (parts[2], "password"))
formhistory_update_database (priv->db, NULL, parts[0], parts[1]);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
else
{
#if 0
FormhistoryPasswordEntry* entry;
#endif
gchar* data = formhistory_get_login_data (priv->db, webkit_web_frame_get_uri (web_frame));
if (data)
{
g_free (data);
break;
}
#if 0
entry = g_slice_new (FormhistoryPasswordEntry);
/* Domain and form data are freed from infopanel callback*/
entry->form_data = g_strdup (value);
entry->domain = domain;
entry->priv = priv;
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", entry);
#endif
}
#endif
}
g_strfreev (parts);
i++;
}
g_strfreev (inputs);
g_free (value);
}
return FALSE;
}
static void
formhistory_window_object_cleared_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
JSContextRef js_context,
JSObjectRef js_window,
MidoriExtension* extension)
{
const gchar* page_uri;
FormhistoryPasswordEntry* entry;
GtkWidget* view;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!page_uri)
return;
if (!midori_uri_is_http (page_uri) && !g_str_has_prefix (page_uri, "file"))
return;
formhistory_setup_suggestions (web_view, js_context, extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
entry = g_object_get_data (G_OBJECT (web_view), "FormHistoryPasswordEntry");
if (entry)
{
const gchar* message = _("Remember password on this page?");
view = midori_browser_get_current_tab (midori_app_get_browser (
midori_extension_get_app (extension)));
midori_view_add_info_bar (MIDORI_VIEW (view), GTK_MESSAGE_QUESTION, message,
G_CALLBACK (formhistory_remember_password_response), entry,
_("Remember"), GTK_RESPONSE_ACCEPT,
_("Not now"), GTK_RESPONSE_IGNORE,
_("Never for this page"), GTK_RESPONSE_CANCEL, NULL);
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", NULL);
}
#endif
}
#if WEBKIT_CHECK_VERSION (1, 3, 8)
static gchar*
formhistory_decrypt (const gchar* data,
const gchar* password)
{
/* TODO: Implement persistent storage/ keyring support */
return NULL;
}
static void
formhistory_fill_login_data (JSContextRef js_context,
FormHistoryPriv* priv,
const gchar* data)
{
gchar* decrypted_data = NULL;
guint i = 0;
GString *script;
gchar** inputs;
/* Handle case that user dont want to store password */
if (!strncmp (data, "never", 5))
return;
#if 0
if (!formhistory_check_master_password (NULL, priv))
return;
#endif
if (!(decrypted_data = formhistory_decrypt (data, priv->master_password)))
return;
script = g_string_new ("");
inputs = g_strsplit (decrypted_data, "|||", 0);
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
g_string_append_printf (script, "node = null;"
"node = document.getElementById ('%s');"
"if (!node) { node = document.getElementsByName ('%s')[0]; }"
"if (node && node.type == '%s') { node.value = '%s'; }",
parts[0], parts[0], parts[2], parts[1]);
}
g_strfreev (parts);
i++;
}
g_free (decrypted_data);
g_strfreev (inputs);
g_free (sokoke_js_script_eval (js_context, script->str, NULL));
g_string_free (script, TRUE);
}
static void
formhistory_frame_loaded_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
MidoriExtension* extension)
{
const gchar* page_uri;
const gchar* count_request;
FormHistoryPriv* priv;
JSContextRef js_context;
gchar* data;
gchar* count;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!page_uri)
return;
count_request = "document.querySelectorAll('input[type=password]').length";
js_context = webkit_web_frame_get_global_context (web_frame);
count = sokoke_js_script_eval (js_context, count_request, NULL);
if (count && count[0] == '0')
{
g_free (count);
return;
}
g_free (count);
priv = g_object_get_data (G_OBJECT (extension), "priv");
data = formhistory_get_login_data (priv->db, webkit_web_frame_get_uri (web_frame));
if (!data)
return;
formhistory_fill_login_data (js_context, priv, data);
g_free (data);
}
#endif
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser);
static void
formhistory_add_tab_cb (MidoriBrowser* browser,
MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_connect (web_view, "window-object-cleared",
G_CALLBACK (formhistory_window_object_cleared_cb), extension);
g_signal_connect (web_view, "navigation-policy-decision-requested",
G_CALLBACK (formhistory_navigation_decision_cb), extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
g_signal_connect (web_view, "onload-event",
G_CALLBACK (formhistory_frame_loaded_cb), extension);
#endif
}
static void
formhistory_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkAccelGroup* acg = gtk_accel_group_new ();
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action = gtk_action_new ("FormHistoryToggleState",
_("Toggle form history state"),
_("Activate or deactivate form history for the current tab."), NULL);
gtk_window_add_accel_group (GTK_WINDOW (browser), acg);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", extension);
g_signal_connect (action, "activate",
G_CALLBACK (formhistory_toggle_state_cb), browser);
gtk_action_group_add_action_with_accel (action_group, action, "<Ctrl><Shift>F");
gtk_action_set_accel_group (action, acg);
gtk_action_connect_accelerator (action);
if (midori_extension_get_boolean (extension, "always-load"))
{
GList* tabs = midori_browser_get_tabs (browser);
for (; tabs; tabs = g_list_next (tabs))
formhistory_add_tab_cb (browser, tabs->data, extension);
g_list_free (tabs);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
g_signal_connect (extension, "deactivate",
G_CALLBACK (formhistory_deactivate_cb), browser);
}
static void
formhistory_deactivate_tab (MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_window_object_cleared_cb, extension);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_navigation_decision_cb, extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
g_signal_handlers_disconnect_by_func (
web_view, formhistory_frame_loaded_cb, extension);
#endif
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser)
{
MidoriApp* app = midori_extension_get_app (extension);
FormHistoryPriv* priv = g_object_get_data (G_OBJECT (extension), "priv");
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action;
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
g_signal_handlers_disconnect_by_func (
extension, formhistory_deactivate_cb, browser);
g_signal_handlers_disconnect_by_func (
app, formhistory_app_add_browser_cb, extension);
GList* tabs = midori_browser_get_tabs (browser);
for (; tabs; tabs = g_list_next (tabs))
formhistory_deactivate_tab (tabs->data, extension);
g_list_free (tabs);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", NULL);
action = gtk_action_group_get_action (action_group, "FormHistoryToggleState");
if (action != NULL)
{
gtk_action_group_remove_action (action_group, action);
g_object_unref (action);
}
formhistory_private_destroy (priv);
}
static FormHistoryPriv*
formhistory_new (const gchar* config_dir)
{
gchar* filename;
sqlite3* db;
char* errmsg = NULL, *errmsg2 = NULL;
FormHistoryPriv* priv = formhistory_private_new ();
priv->master_password = NULL;
priv->master_password_canceled = 0;
formhistory_construct_popup_gui (priv);
filename = g_build_filename (config_dir, "forms.db", NULL);
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
g_warning (_("Failed to open database: %s\n"), sqlite3_errmsg (db));
sqlite3_close (db);
}
g_free (filename);
if ((sqlite3_exec (db, "CREATE TABLE IF NOT EXISTS "
"forms (domain text, field text, value text)",
NULL, NULL, &errmsg) == SQLITE_OK))
{
sqlite3_exec (db,
/* "PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY" */
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
NULL, NULL, &errmsg);
priv->db = db;
}
else
{
if (errmsg)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg);
sqlite3_free (errmsg);
if (errmsg2)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg2);
sqlite3_free (errmsg2);
}
}
sqlite3_close (db);
}
return priv;
}
static void
formhistory_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
const gchar* config_dir = midori_extension_get_config_dir (extension);
FormHistoryPriv* priv = formhistory_new (config_dir);
KatzeArray* browsers = katze_object_get_object (app, "browsers");
MidoriBrowser* browser;
g_object_set_data (G_OBJECT (extension), "priv", priv);
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
formhistory_app_add_browser_cb (app, browser, extension);
g_signal_connect (app, "add-browser",
G_CALLBACK (formhistory_app_add_browser_cb), extension);
g_object_unref (browsers);
}
static void
formhistory_preferences_response_cb (GtkWidget* dialog,
gint response_id,
MidoriExtension* extension)
{
GtkWidget* checkbox;
gboolean old_state;
gboolean new_state;
MidoriApp* app;
KatzeArray* browsers;
MidoriBrowser* browser;
if (response_id == GTK_RESPONSE_APPLY)
{
checkbox = g_object_get_data (G_OBJECT (dialog), "always-load-checkbox");
new_state = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
old_state = midori_extension_get_boolean (extension, "always-load");
if (old_state != new_state)
{
midori_extension_set_boolean (extension, "always-load", new_state);
app = midori_extension_get_app (extension);
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
{
GList* tabs = midori_browser_get_tabs (browser);
for (; tabs; tabs = g_list_next (tabs))
formhistory_deactivate_tab (tabs->data, extension);
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
if (new_state)
{
for (; tabs; tabs = g_list_next (tabs))
formhistory_add_tab_cb (browser, tabs->data, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
g_list_free (tabs);
}
}
}
gtk_widget_destroy (dialog);
}
static void
formhistory_preferences_cb (MidoriExtension* extension)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* checkbox;
dialog = gtk_dialog_new ();
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
checkbox = gtk_check_button_new_with_label (_("Only activate form history via hotkey (Ctrl+Shift+F) per tab"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
!midori_extension_get_boolean (extension, "always-load"));
g_object_set_data (G_OBJECT (dialog), "always-load-checkbox", checkbox);
gtk_container_add (GTK_CONTAINER (content_area), checkbox);
/* FIXME: Add pref to disable password manager */
g_signal_connect (dialog,
"response",
G_CALLBACK (formhistory_preferences_response_cb),
extension);
gtk_widget_show_all (dialog);
}
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser)
{
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
MidoriExtension* extension = g_object_get_data (G_OBJECT (browser), "FormHistoryExtension");
GtkWidget* web_view = midori_view_get_web_view (view);
if (g_signal_handler_find (web_view, G_SIGNAL_MATCH_FUNC,
g_signal_lookup ("window-object-cleared", MIDORI_TYPE_VIEW), 0, NULL,
formhistory_window_object_cleared_cb, extension))
{
formhistory_deactivate_tab (view, extension);
}
else
formhistory_add_tab_cb (browser, view, extension);
}
static void
test_formhistory_login (void)
{
gchar* config_dir = midori_paths_get_extension_config_dir ("formhistory");
FormHistoryPriv* priv = formhistory_new (config_dir);
g_free (formhistory_get_login_data (priv->db, "http://example.com"));
g_free (formhistory_get_login_data (priv->db, "http://beispiel.de"));
formhistory_update_database (priv->db, "http://example.com", "MidoriPasswordManager", "lalelu");
formhistory_update_database (priv->db, NULL, "spam", "eggs");
g_free (formhistory_get_login_data (priv->db, "http://example.com"));
g_free (formhistory_get_login_data (priv->db, "http://beispiel.de"));
g_free (config_dir);
formhistory_private_destroy (priv);
}
void
extension_test (void)
{
g_test_add_func ("/extensions/formhistory/login", test_formhistory_login);
}
MidoriExtension*
extension_init (void)
{
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Form history filler"),
"description", _("Stores history of entered form data"),
"version", "2.0" MIDORI_VERSION_SUFFIX,
"authors", "Alexander V. Butenko <a.butenka@gmail.com>",
NULL);
midori_extension_install_boolean (extension, "always-load", TRUE);
g_signal_connect (extension, "activate",
G_CALLBACK (formhistory_activate_cb), NULL);
g_signal_connect (extension, "open-preferences",
G_CALLBACK (formhistory_preferences_cb), NULL);
return extension;
}

View file

@ -18,6 +18,8 @@ namespace HistoryList {
enum TabTreeCells { enum TabTreeCells {
TREE_CELL_PIXBUF, TREE_CELL_PIXBUF,
TREE_CELL_STRING, TREE_CELL_STRING,
TREE_CELL_FG,
TREE_CELL_BG,
TREE_CELL_POINTER, TREE_CELL_POINTER,
TREE_CELL_COUNT TREE_CELL_COUNT
} }
@ -83,6 +85,8 @@ namespace HistoryList {
store.append (out iter); store.append (out iter);
store.set (iter, TabTreeCells.TREE_CELL_PIXBUF, icon, store.set (iter, TabTreeCells.TREE_CELL_PIXBUF, icon,
TabTreeCells.TREE_CELL_STRING, title, TabTreeCells.TREE_CELL_STRING, title,
TabTreeCells.TREE_CELL_FG, view.fg_color,
TabTreeCells.TREE_CELL_BG, view.bg_color,
TabTreeCells.TREE_CELL_POINTER, view); TabTreeCells.TREE_CELL_POINTER, view);
} }
} }
@ -109,7 +113,8 @@ namespace HistoryList {
this.hbox.pack_start (sw, true, true, 0); this.hbox.pack_start (sw, true, true, 0);
var store = new Gtk.ListStore (TabTreeCells.TREE_CELL_COUNT, var store = new Gtk.ListStore (TabTreeCells.TREE_CELL_COUNT,
typeof (Gdk.Pixbuf), typeof (string), typeof (void*)); typeof (Gdk.Pixbuf), typeof (string),
typeof (Gdk.Color), typeof (Gdk.Color), typeof (void*));
this.insert_rows (store); this.insert_rows (store);
@ -123,18 +128,22 @@ namespace HistoryList {
this.treeview.insert_column_with_attributes ( this.treeview.insert_column_with_attributes (
-1, "Icon", -1, "Icon",
new CellRendererPixbuf (), "pixbuf", TabTreeCells.TREE_CELL_PIXBUF); new CellRendererPixbuf (), "pixbuf", TabTreeCells.TREE_CELL_PIXBUF,
"cell-background-gdk", TabTreeCells.TREE_CELL_BG);
this.treeview.insert_column_with_attributes ( this.treeview.insert_column_with_attributes (
-1, "Title", -1, "Title",
new CellRendererText (), "text", TabTreeCells.TREE_CELL_STRING); new CellRendererText (), "text", TabTreeCells.TREE_CELL_STRING,
"foreground-gdk", TabTreeCells.TREE_CELL_FG,
"cell-background-gdk", TabTreeCells.TREE_CELL_BG);
this.show_all ();
Requisition requisition; Requisition requisition;
int height; int height;
int max_lines = 10; int max_lines = 10;
#if HAVE_GTK3 #if HAVE_GTK3
requisition = Requisition(); requisition = Requisition();
this.treeview.get_preferred_width(out requisition.width, null); this.treeview.get_preferred_size(out requisition, null);
this.treeview.get_preferred_height(out requisition.height, null);
#else #else
this.treeview.size_request (out requisition); this.treeview.size_request (out requisition);
#endif #endif
@ -145,8 +154,6 @@ namespace HistoryList {
height = requisition.height + 2; height = requisition.height + 2;
} }
sw.set_size_request (320, height); sw.set_size_request (320, height);
this.show_all ();
} }
public override void make_update () { public override void make_update () {
@ -225,7 +232,7 @@ namespace HistoryList {
} }
} }
private class PreferencesDialog : Dialog { private class PreferencesDialog : Gtk.Dialog {
protected Manager hl_manager; protected Manager hl_manager;
protected ComboBox closing_behavior; protected ComboBox closing_behavior;
@ -243,7 +250,7 @@ namespace HistoryList {
this.response.connect (response_cb); this.response.connect (response_cb);
} }
private void response_cb (Dialog source, int response_id) { private void response_cb (Gtk.Dialog source, int response_id) {
switch (response_id) { switch (response_id) {
case ResponseType.APPLY: case ResponseType.APPLY:
int value; int value;
@ -304,6 +311,12 @@ namespace HistoryList {
table.attach_defaults (this.closing_behavior, 1, 2, 0, 1); table.attach_defaults (this.closing_behavior, 1, 2, 0, 1);
#if !HAVE_WIN32
var proxy = Katze.property_proxy (this.hl_manager.get_app ().settings, "flash-window-on-new-bg-tabs", null);
(proxy as Gtk.Button).label = _("Flash window on background tabs");
table.attach_defaults (proxy, 0, 2, 1, 2);
#endif
#if HAVE_GTK3 #if HAVE_GTK3
(get_content_area() as Gtk.Box).pack_start (table, false, true, 0); (get_content_area() as Gtk.Box).pack_start (table, false, true, 0);
#else #else
@ -331,15 +344,27 @@ namespace HistoryList {
this.closing_behavior = this.get_integer ("TabClosingBehavior"); this.closing_behavior = this.get_integer ("TabClosingBehavior");
} }
public bool is_key_a_modifier (Gdk.EventKey event_key) {
#if HAVE_WIN32
/* On win is_modifier check does not seem to work */
if (event_key.keyval == Gdk.keyval_from_name("Control_L"))
return true;
#else
if (event_key.is_modifier > 0)
return true;
#endif
return false;
}
public bool key_press (Gdk.EventKey event_key) { public bool key_press (Gdk.EventKey event_key) {
if (event_key.is_modifier > 0) { if (is_key_a_modifier (event_key)) {
this.modifier_count++; this.modifier_count++;
} }
return false; return false;
} }
public bool key_release (Gdk.EventKey event_key, Browser browser) { public bool key_release (Gdk.EventKey event_key, Browser browser) {
if (event_key.is_modifier > 0) { if (is_key_a_modifier (event_key)) {
this.modifier_count--; this.modifier_count--;
} }
if (this.modifier_count == 0 || event_key.keyval == this.escKeyval) { if (this.modifier_count == 0 || event_key.keyval == this.escKeyval) {
@ -462,7 +487,7 @@ namespace HistoryList {
tab_added (browser, tab); tab_added (browser, tab);
browser.add_tab.connect (tab_added); browser.add_tab.connect (tab_added);
browser.remove_tab.connect (tab_removed); browser.remove_tab.connect (tab_removed);
browser.notify["tab"].connect (this.tab_changed); browser.switch_tab.connect (this.tab_changed);
} }
void browser_removed (Midori.Browser browser) { void browser_removed (Midori.Browser browser) {
@ -491,7 +516,7 @@ namespace HistoryList {
browser.add_tab.disconnect (tab_added); browser.add_tab.disconnect (tab_added);
browser.remove_tab.disconnect (tab_removed); browser.remove_tab.disconnect (tab_removed);
browser.notify["tab"].disconnect (this.tab_changed); browser.switch_tab.disconnect (this.tab_changed);
} }
void tab_added (Midori.Browser browser, Midori.View view) { void tab_added (Midori.Browser browser, Midori.View view) {
@ -505,6 +530,11 @@ namespace HistoryList {
list.remove (view); list.remove (view);
list_new.remove (view); list_new.remove (view);
Midori.View? current_view = browser.tab as Midori.View;
if (current_view != view)
return;
if (this.closing_behavior == TabClosingBehavior.LAST || this.closing_behavior == TabClosingBehavior.NEW) { if (this.closing_behavior == TabClosingBehavior.LAST || this.closing_behavior == TabClosingBehavior.NEW) {
browser.set_data<Midori.View?> ("history-list-last-change", null); browser.set_data<Midori.View?> ("history-list-last-change", null);
@ -520,21 +550,18 @@ namespace HistoryList {
} }
} }
void tab_changed (GLib.Object window, GLib.ParamSpec pspec) { void tab_changed (Midori.View? old_view, Midori.View? new_view) {
if(this.ignoreNextChange) { if(this.ignoreNextChange) {
this.ignoreNextChange = false; this.ignoreNextChange = false;
} else { } else {
Midori.Browser browser = window as Midori.Browser; Midori.Browser? browser = Midori.Browser.get_for_widget (new_view);
Midori.View view = null; Midori.View? last_view
Midori.View last_view = null; = browser.get_data<Midori.View?> ("history-list-last-change");
browser.get ("tab", ref view);
last_view = browser.get_data<Midori.View?> ("history-list-last-change");
if (last_view != null) { if (last_view != null) {
this.tab_list_resort (browser, last_view); this.tab_list_resort (browser, last_view);
} }
browser.set_data<Midori.View?> ("history-list-last-change", view); browser.set_data<Midori.View?> ("history-list-last-change", new_view);
} }
} }

View file

@ -11,49 +11,207 @@
*/ */
#include <midori/midori.h> #include <midori/midori.h>
#include <math.h>
typedef struct _MouseGesture MouseGesture; typedef struct _MouseGesture MouseGesture;
typedef enum _MouseButton MouseButton; typedef enum _MouseButton MouseButton;
enum _MouseButton { enum _MouseButton
{
MOUSE_BUTTON_LEFT = 1, MOUSE_BUTTON_LEFT = 1,
MOUSE_BUTTON_RIGHT = 3, MOUSE_BUTTON_RIGHT = 3,
MOUSE_BUTTON_MIDDLE = 2, MOUSE_BUTTON_MIDDLE = 2,
MOUSE_BUTTON_UNSET = 0 MOUSE_BUTTON_UNSET = 0
}; };
struct MouseGestureNode { /* equivalent to the angle measured anticlockwise from east, divided by 45 or pi/4 */
typedef enum
{
STROKE_EAST = 0,
STROKE_NORTHEAST,
STROKE_NORTH,
STROKE_NORTHWEST,
STROKE_WEST,
STROKE_SOUTHWEST,
STROKE_SOUTH,
STROKE_SOUTHEAST,
STROKE_NONE,
} MouseGestureDirection;
static const gchar* direction_names[]=
{
"E",
"NE",
"N",
"NW",
"W",
"SW",
"S",
"SE",
"NONE",
};
#define N_DIRECTIONS 8
#define DEVIANCE (15 * M_PI / 180)
#define MINLENGTH 30
char** config_actions = NULL;
MouseGestureDirection** config_gestures = NULL;
const char* default_actions[]=
{
"TabClose",
"Reload",
"TabNew",
"Stop",
"Forward",
"Back",
NULL
};
const MouseGestureDirection default_gesture_strokes[] =
{
STROKE_SOUTH, STROKE_EAST, STROKE_NONE,
STROKE_SOUTH, STROKE_WEST, STROKE_NONE,
STROKE_SOUTH, STROKE_NONE,
STROKE_NORTH, STROKE_NONE,
STROKE_EAST, STROKE_NONE,
STROKE_WEST, STROKE_NONE,
STROKE_NONE,
};
const MouseGestureDirection* default_gestures[] =
{
&default_gesture_strokes[0],
&default_gesture_strokes[3],
&default_gesture_strokes[6],
&default_gesture_strokes[8],
&default_gesture_strokes[10],
&default_gesture_strokes[12],
&default_gesture_strokes[14],
};
static gboolean
parse_direction (const char* str, MouseGestureDirection* dir)
{
int i;
for (i = 0; i < N_DIRECTIONS; i++)
{
if(!strcmp(str, direction_names[i]))
{
*dir = i;
return TRUE;
}
}
return FALSE;
}
static gboolean
strokes_equal (const MouseGestureDirection* a, const MouseGestureDirection* b)
{
int i;
for (i = 0; a[i] != STROKE_NONE && b[i] != STROKE_NONE; i++)
{
if(a[i] != b[i])
return FALSE;
}
return a[i] == b[i];
}
struct MouseGestureNode
{
double x; double x;
double y; double y;
}; };
struct _MouseGesture { static guint
dist_sqr (guint x1, guint y1, guint x2, guint y2)
{
guint xdiff = abs(x1 - x2);
guint ydiff = abs(y1 - y2);
return xdiff * xdiff + ydiff * ydiff;
}
static float
get_angle_for_direction (MouseGestureDirection direction)
{
return direction * 2 * M_PI / N_DIRECTIONS;
}
static MouseGestureDirection
nearest_direction_for_angle (float angle)
{
/* move halfway to the next direction so we can floor to round */
angle += M_PI / N_DIRECTIONS;
/* ensure we stay within [0, 2pi) */
if (angle >= 2 * M_PI)
angle -= 2 * M_PI;
return (MouseGestureDirection)((angle * N_DIRECTIONS) / (2* M_PI));
}
static gboolean
vector_follows_direction (float angle, float distance, MouseGestureDirection direction)
{
if (direction == STROKE_NONE)
return distance < MINLENGTH / 2;
float dir_angle = get_angle_for_direction (direction);
if (fabsf(angle - dir_angle) < DEVIANCE || fabsf(angle - dir_angle + 2 * M_PI) < DEVIANCE)
return TRUE;
if(distance < MINLENGTH / 2)
return TRUE;
return FALSE;
}
/* returns the angle in the range [0, 2pi) (anticlockwise from east) from point 1 to 2 */
static float
get_angle_between_points (guint x1, guint y1, guint x2, guint y2)
{
float distance = sqrtf (dist_sqr (x1, y1, x2, y2));
/* compute the angle of the vector from a to b */
float cval=((signed int)x2 - (signed int)x1) / distance;
float angle = acosf (cval);
if(y2 > y1)
angle = 2 * M_PI - angle;
return angle;
}
#define N_NODES 8
struct _MouseGesture
{
MouseButton button; MouseButton button;
struct MouseGestureNode start; MouseGestureDirection strokes[N_NODES + 1];
struct MouseGestureNode middle; struct MouseGestureNode locations[N_NODES];
struct MouseGestureNode end; struct MouseGestureNode last_pos;
float last_distance;
/* the index of the location to be filled next */
guint count;
MouseButton last; MouseButton last;
}; };
#define DEVIANCE 20
#define MINLENGTH 50
MouseGesture *gesture = NULL; MouseGesture *gesture = NULL;
void mouse_gesture_clear (MouseGesture *g) static void
mouse_gesture_clear (MouseGesture *g)
{ {
g->start.x = 0; memset(g->locations, 0, sizeof(g->locations));
g->start.y = 0; g->strokes[0] = STROKE_NONE;
g->middle.x = 0; g->count = 0;
g->middle.y = 0; g->last_distance = 0;
g->end.x = 0;
g->end.y = 0;
g->last = MOUSE_BUTTON_UNSET; g->last = MOUSE_BUTTON_UNSET;
} }
MouseGesture* mouse_gesture_new (void) MouseGesture* mouse_gesture_new (void)
{ {
MouseGesture* g = g_new (MouseGesture, 1); MouseGesture* g = g_slice_new (MouseGesture);
mouse_gesture_clear (g); mouse_gesture_clear (g);
return g; return g;
@ -68,10 +226,11 @@ mouse_gestures_button_press_event_cb (GtkWidget* web_view,
{ {
/* If the gesture was previously cleaned, /* If the gesture was previously cleaned,
start a new gesture and coordinates. */ start a new gesture and coordinates. */
if (gesture->last == MOUSE_BUTTON_UNSET) if (gesture->count == MOUSE_BUTTON_UNSET)
{ {
gesture->start.x = event->button.x; gesture->locations[gesture->count].x = event->button.x;
gesture->start.y = event->button.y; gesture->locations[gesture->count].y = event->button.y;
gesture->last_pos = gesture->locations[gesture->count];
gesture->last = event->button.button; gesture->last = event->button.button;
} }
return TRUE; return TRUE;
@ -85,28 +244,59 @@ mouse_gestures_motion_notify_event_cb (GtkWidget* web_view,
GdkEvent* event, GdkEvent* event,
MidoriBrowser* browser) MidoriBrowser* browser)
{ {
/* wait until a button has been pressed */
if (gesture->last != MOUSE_BUTTON_UNSET) if (gesture->last != MOUSE_BUTTON_UNSET)
{ {
guint x, y; guint x, y, oldx, oldy;
float angle, distance;
MouseGestureDirection old_direction, new_direction;
x = event->motion.x; x = event->motion.x;
y = event->motion.y; y = event->motion.y;
oldx = gesture->locations[gesture->count].x;
oldy = gesture->locations[gesture->count].y;
if ((gesture->start.x - x < DEVIANCE && gesture->start.x - x > -DEVIANCE) || old_direction = gesture->strokes[gesture->count];
(gesture->start.y - y < DEVIANCE && gesture->start.y - y > -DEVIANCE))
angle = get_angle_between_points (oldx, oldy, x, y);
distance = sqrtf (dist_sqr (oldx, oldy, x, y));
/* wait until minimum distance has been reached to set an initial direction. */
if (old_direction == STROKE_NONE)
{ {
gesture->middle.x = x; if (distance >= MINLENGTH)
gesture->middle.y = y;
return TRUE;
}
else if ((gesture->middle.x - x < DEVIANCE && gesture->middle.x - x > -DEVIANCE) ||
(gesture->middle.y - y < DEVIANCE && gesture->middle.y - y > -DEVIANCE))
{ {
gesture->end.x = x; gesture->strokes[gesture->count] = nearest_direction_for_angle (angle);
gesture->end.y = y; if(midori_debug ("adblock:match"))
return TRUE; g_debug ("detected %s\n", direction_names[gesture->strokes[gesture->count]]);
} }
} }
else if (!vector_follows_direction (angle, distance, old_direction)
|| distance < gesture->last_distance)
{
/* if path curves or we've reversed our movement, try to detect a new direction */
angle = get_angle_between_points (gesture->last_pos.x, gesture->last_pos.y, x, y);
new_direction = nearest_direction_for_angle (angle);
if (new_direction != old_direction && gesture->count + 1 < N_NODES)
{
/* record this node and return to an indeterminate direction */
gesture->count++;
gesture->strokes[gesture->count] = STROKE_NONE;
gesture->locations[gesture->count].x = x;
gesture->locations[gesture->count].y = y;
gesture->last_distance = 0;
}
}
else if(distance > gesture->last_distance)
{
/* if following the same direction, store the progress along it for later divergence checks */
gesture->last_pos.x = x;
gesture->last_pos.y = y;
gesture->last_distance = distance;
}
return TRUE;
}
return FALSE; return FALSE;
} }
@ -125,67 +315,29 @@ mouse_gestures_button_release_event_cb (GtkWidget* web_view,
GdkEventButton* event, GdkEventButton* event,
MidoriView* view) MidoriView* view)
{ {
/* All mouse gestures will use this mouse button */ int i;
if (gesture->last == gesture->button)
if (gesture->strokes[gesture->count] != STROKE_NONE)
{ {
/* The initial horizontal move is between the bounds */ gesture->count++;
if ((gesture->middle.x - gesture->start.x < DEVIANCE) && gesture->strokes[gesture->count] = STROKE_NONE;
(gesture->middle.x - gesture->start.x > -DEVIANCE))
{
/* We initially moved down more than MINLENGTH pixels */
if (gesture->middle.y > gesture->start.y + MINLENGTH)
{
/* Then we the final vertical move is between the bounds and
we moved right more than MINLENGTH pixels */
if ((gesture->middle.y - gesture->end.y < DEVIANCE) &&
(gesture->middle.y - gesture->end.y > -DEVIANCE) &&
(gesture->end.x > gesture->middle.x + MINLENGTH))
/* We moved down then right: close the tab */
return mouse_gestures_activate_action (view, "TabClose");
/* Then we the final vertical move is between the bounds and
we moved left more than MINLENGTH pixels */
else if ((gesture->middle.y - gesture->end.y < DEVIANCE) &&
(gesture->middle.y - gesture->end.y > -DEVIANCE) &&
(gesture->end.x + MINLENGTH < gesture->middle.x))
/* We moved down then left: reload */
return mouse_gestures_activate_action (view, "Reload");
/* The end node was never updated, we only did a vertical move */
else if(gesture->end.y == 0 && gesture->end.x == 0)
/* We moved down then: create a new tab */
return mouse_gestures_activate_action (view, "TabNew");
} }
/* We initially moved up more than MINLENGTH pixels */
else if (gesture->middle.y + MINLENGTH < gesture->start.y) const MouseGestureDirection** gestures = config_gestures ?
(const MouseGestureDirection**)config_gestures :
default_gestures;
const gchar** actions = config_actions ? (const char**)config_actions : default_actions;
for(i = 0; gestures[i][0] != STROKE_NONE; i++)
{ {
/* The end node was never updated, we only did a vertical move */ if(strokes_equal (gesture->strokes, gestures[i]))
if (gesture->end.y == 0 && gesture->end.x == 0)
/* We moved up: stop */
return mouse_gestures_activate_action (view, "Stop");
}
}
/* The initial horizontal move is between the bounds */
else if ((gesture->middle.y - gesture->start.y < DEVIANCE) &&
(gesture->middle.y - gesture->start.y > -DEVIANCE))
{ {
/* We initially moved right more than MINLENGTH pixels */
if (gesture->middle.x > gesture->start.x + MINLENGTH)
{
/* The end node was never updated, we only did an horizontal move */
if (gesture->end.x == 0 && gesture->end.y == 0)
/* We moved right: forward */
return mouse_gestures_activate_action (view, "Forward");
}
/* We initially moved left more than MINLENGTH pixels */
else if (gesture->middle.x + MINLENGTH < gesture->start.x)
{
/* The end node was never updated, we only did an horizontal move */
if (gesture->end.x == 0 && gesture->end.y == 0)
/* We moved left: back */
return mouse_gestures_activate_action (view, "Back");
}
}
mouse_gesture_clear (gesture); mouse_gesture_clear (gesture);
return mouse_gestures_activate_action (view, actions[i]);
} }
}
mouse_gesture_clear (gesture);
if (MIDORI_EVENT_CONTEXT_MENU (event)) if (MIDORI_EVENT_CONTEXT_MENU (event))
{ {
@ -199,6 +351,62 @@ mouse_gestures_button_release_event_cb (GtkWidget* web_view,
return FALSE; return FALSE;
} }
static void
mouse_gestures_load_config (MidoriExtension* extension)
{
int i;
gchar* config_file;
gsize n_keys;
gchar** keys;
GKeyFile* keyfile;
config_file = g_build_filename (midori_extension_get_config_dir (extension),
"gestures", NULL);
keyfile = g_key_file_new ();
g_key_file_load_from_file (keyfile, config_file, G_KEY_FILE_NONE, NULL);
g_free (config_file);
if (!keyfile)
return;
keys = g_key_file_get_keys (keyfile, "gestures", &n_keys, NULL);
if (!keys)
return;
if(config_gestures)
{
g_strfreev ((gchar**)config_gestures);
g_strfreev (config_actions);
}
config_gestures = g_malloc ((n_keys + 1) * sizeof (MouseGestureDirection*));
config_actions = g_malloc (n_keys * sizeof (gchar*));
for(i = 0; keys[i]; i++)
{
gsize n_strokes;
int j;
gchar** stroke_strings = g_key_file_get_string_list (keyfile, "gestures", keys[i], &n_strokes,
NULL);
config_gestures[i] = g_malloc ((n_strokes + 1) * sizeof (MouseGestureDirection));
for (j = 0; j < n_strokes; j++)
{
if (!parse_direction (stroke_strings[j], &config_gestures[i][j]))
g_warning ("mouse-gestures: failed to parse direction \"%s\"\n", stroke_strings[j]);
}
config_gestures[i][j] = STROKE_NONE;
config_actions[i] = keys[i];
g_strfreev (stroke_strings);
}
config_gestures[i] = g_malloc (sizeof (MouseGestureDirection));
config_gestures[i][0] = STROKE_NONE;
g_free (keys);
g_key_file_free (keyfile);
}
static void static void
mouse_gestures_add_tab_cb (MidoriBrowser* browser, mouse_gestures_add_tab_cb (MidoriBrowser* browser,
MidoriView* view, MidoriView* view,
@ -220,21 +428,15 @@ static void
mouse_gestures_deactivate_cb (MidoriExtension* extension, mouse_gestures_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser); MidoriBrowser* browser);
static void
mouse_gestures_add_tab_foreach_cb (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
mouse_gestures_add_tab_cb (browser, view, extension);
}
static void static void
mouse_gestures_app_add_browser_cb (MidoriApp* app, mouse_gestures_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser, MidoriBrowser* browser,
MidoriExtension* extension) MidoriExtension* extension)
{ {
midori_browser_foreach (browser, GList* tabs = midori_browser_get_tabs (browser);
(GtkCallback)mouse_gestures_add_tab_foreach_cb, extension); for (; tabs; tabs = g_list_next (tabs))
mouse_gestures_add_tab_cb (browser, tabs->data, extension);
g_list_free (tabs);
g_signal_connect (browser, "add-tab", g_signal_connect (browser, "add-tab",
G_CALLBACK (mouse_gestures_add_tab_cb), extension); G_CALLBACK (mouse_gestures_add_tab_cb), extension);
g_signal_connect (extension, "deactivate", g_signal_connect (extension, "deactivate",
@ -269,10 +471,19 @@ mouse_gestures_deactivate_cb (MidoriExtension* extension,
app, mouse_gestures_app_add_browser_cb, extension); app, mouse_gestures_app_add_browser_cb, extension);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
browser, mouse_gestures_add_tab_cb, extension); browser, mouse_gestures_add_tab_cb, extension);
midori_browser_foreach (browser,
(GtkCallback)mouse_gestures_deactivate_tabs, browser);
g_free (gesture); GList* tabs = midori_browser_get_tabs (browser);
for (; tabs; tabs = g_list_next (tabs))
mouse_gestures_deactivate_tabs (tabs->data, browser);
g_list_free (tabs);
g_slice_free (MouseGesture, gesture);
if(config_gestures)
{
g_strfreev ((gchar**)config_gestures);
config_gestures = NULL;
g_strfreev (config_actions);
config_actions = NULL;
}
} }
static void static void
@ -284,6 +495,7 @@ mouse_gestures_activate_cb (MidoriExtension* extension,
gesture = mouse_gesture_new (); gesture = mouse_gesture_new ();
gesture->button = midori_extension_get_integer (extension, "button"); gesture->button = midori_extension_get_integer (extension, "button");
mouse_gestures_load_config (extension);
browsers = katze_object_get_object (app, "browsers"); browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers) KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
@ -300,9 +512,10 @@ extension_init (void)
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION, MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Mouse Gestures"), "name", _("Mouse Gestures"),
"description", _("Control Midori by moving the mouse"), "description", _("Control Midori by moving the mouse"),
"version", "0.1" MIDORI_VERSION_SUFFIX, "version", "0.2" MIDORI_VERSION_SUFFIX,
"authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL); "authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL);
midori_extension_install_integer (extension, "button", MOUSE_BUTTON_RIGHT); midori_extension_install_integer (extension, "button", MOUSE_BUTTON_RIGHT);
midori_extension_install_integer (extension, "actions", MOUSE_BUTTON_RIGHT);
g_signal_connect (extension, "activate", g_signal_connect (extension, "activate",
G_CALLBACK (mouse_gestures_activate_cb), NULL); G_CALLBACK (mouse_gestures_activate_cb), NULL);

View file

@ -0,0 +1,77 @@
/*
Copyright (C) 2012 André Stösel <andre@stoesel.de>
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.
*/
#if HAVE_WEBKIT_1_3_8
namespace NSPlugins {
private int active_plugins = 0;
private class Extension : Midori.Extension {
protected WebKit.WebPlugin plugin;
void activated (Midori.App app) {
active_plugins += 1;
this.plugin.set_enabled (true);
app.settings.enable_plugins = active_plugins > 0;
}
void deactivated () {
Midori.App app = this.get_app ();
active_plugins -= 1;
this.plugin.set_enabled (false);
app.settings.enable_plugins = active_plugins > 0;
}
internal Extension (WebKit.WebPlugin plugin) {
string desc = plugin.get_description ();
try {
var regex = new Regex ("<a.+href.+>(.+)</a>");
desc = regex.replace (desc, -1, 0, "<u>\\1</u>");
desc = desc.replace ("<br>", "\n");
}
catch (Error error) { }
GLib.Object (stock_id: Midori.Stock.PLUGINS,
name: plugin.get_name (),
description: desc,
use_markup: true,
key: GLib.Path.get_basename (plugin.get_path ()),
version: "(%s)".printf ("Netscape plugins"),
authors: "");
this.plugin = plugin;
this.plugin.set_enabled (false);
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
}
#endif
public Katze.Array? extension_init () {
#if HAVE_WEBKIT_1_3_8
if (!Midori.WebSettings.has_plugin_support ())
return null;
var extensions = new Katze.Array( typeof (Midori.Extension));
WebKit.WebPluginDatabase pdb = WebKit.get_web_plugin_database ();
SList<WebKit.WebPlugin> plugins = pdb.get_plugins ();
foreach (WebKit.WebPlugin plugin in plugins) {
if (Midori.WebSettings.skip_plugin (plugin.get_path ()))
continue;
extensions.add_item (new NSPlugins.Extension (plugin));
}
return extensions;
#else
return null;
#endif
}

View file

@ -137,14 +137,6 @@ shortcuts_hotkey_for_action (GtkAction* action,
return FALSE; return FALSE;
} }
static void
shortcuts_preferences_response_cb (GtkWidget* dialog,
gint response,
gpointer data)
{
gtk_widget_destroy (dialog);
}
static GtkWidget* static GtkWidget*
shortcuts_get_preferences_dialog (MidoriExtension* extension) shortcuts_get_preferences_dialog (MidoriExtension* extension)
{ {
@ -180,13 +172,11 @@ shortcuts_get_preferences_dialog (MidoriExtension* extension)
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
#endif #endif
NULL); NULL);
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &dialog);
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES); gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES);
sokoke_widget_get_text_size (dialog, "M", &width, &height); sokoke_widget_get_text_size (dialog, "M", &width, &height);
gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, height * 24); gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, height * 24);
g_signal_connect (dialog, "response", g_signal_connect_swapped (dialog, "response",
G_CALLBACK (shortcuts_preferences_response_cb), NULL); G_CALLBACK (gtk_widget_destroy), dialog);
dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
if ((xfce_heading = sokoke_xfce_header_new ( if ((xfce_heading = sokoke_xfce_header_new (
@ -282,7 +272,7 @@ shortcuts_browser_populate_tool_menu_cb (MidoriBrowser* browser,
{ {
GtkWidget* menuitem; GtkWidget* menuitem;
menuitem = gtk_menu_item_new_with_mnemonic (_("Customize Sh_ortcuts...")); menuitem = gtk_menu_item_new_with_mnemonic (_("Customize Sh_ortcuts"));
g_signal_connect (menuitem, "activate", g_signal_connect (menuitem, "activate",
G_CALLBACK (shortcuts_menu_configure_shortcuts_activate_cb), extension); G_CALLBACK (shortcuts_menu_configure_shortcuts_activate_cb), extension);
gtk_widget_show (menuitem); gtk_widget_show (menuitem);

View file

@ -43,23 +43,26 @@ clock_set_timeout (MidoriBrowser* browser,
static gboolean static gboolean
clock_set_current_time (MidoriBrowser* browser) clock_set_current_time (MidoriBrowser* browser)
{ {
MidoriExtension* extension;
GtkWidget* label;
const gchar* format;
struct tm *tm;
time_t rawtime;
char datestring[60];
guint interval; guint interval;
MidoriExtension* extension = g_object_get_data (G_OBJECT (browser), "clock-extension");
GtkWidget* label = g_object_get_data (G_OBJECT (browser), "clock-label");
const gchar* format = midori_extension_get_string (extension, "format");
extension = g_object_get_data (G_OBJECT (browser), "clock-extension"); #if GLIB_CHECK_VERSION (2, 26, 0)
label = g_object_get_data (G_OBJECT (browser), "clock-label"); GDateTime* date = g_date_time_new_now_local ();
format = midori_extension_get_string (extension, "format"); gint seconds = g_date_time_get_seconds (date);
gchar* pretty = g_date_time_format (date, format);
rawtime = time (NULL); gtk_label_set_label (GTK_LABEL (label), pretty);
tm = localtime (&rawtime); g_free (pretty);
g_date_time_unref (date);
strftime (datestring, 60, format, tm); #else
gtk_label_set_label (GTK_LABEL (label), datestring); time_t rawtime = time (NULL);
struct tm *tm = localtime (&rawtime);
gint seconds = tm->tm_sec;
char date_fmt[512];
strftime (date_fmt, sizeof (date_fmt), format, tm);
gtk_label_set_label (GTK_LABEL (label), date_fmt);
#endif
if (g_strstr_len (format, -1, "%c") if (g_strstr_len (format, -1, "%c")
|| g_strstr_len (format, -1, "%N") || g_strstr_len (format, -1, "%N")
@ -71,7 +74,7 @@ clock_set_current_time (MidoriBrowser* browser)
interval = 1; interval = 1;
else else
/* FIXME: Occasionally there are more than 60 seconds in a minute. */ /* FIXME: Occasionally there are more than 60 seconds in a minute. */
interval = MAX (60 - tm->tm_sec, 1); interval = MAX (60 - seconds, 1);
clock_set_timeout (browser, interval); clock_set_timeout (browser, interval);

View file

@ -66,11 +66,14 @@ statusbar_features_browser_notify_tab_cb (MidoriBrowser* browser,
GtkWidget* combobox) GtkWidget* combobox)
{ {
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser)); MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
gchar* zoom_level_text = g_strdup_printf ("%d%%", gchar* text;
(gint)(midori_view_get_zoom_level (view) * 100));
gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combobox))), if (view == NULL)
zoom_level_text); return;
g_free (zoom_level_text);
text = g_strdup_printf ("%d%%", (gint)(midori_view_get_zoom_level (view) * 100));
gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combobox))), text);
g_free (text);
} }
static void static void
@ -104,6 +107,81 @@ statusbar_features_zoom_level_changed_cb (GtkWidget* combobox,
midori_view_set_zoom_level (view, zoom_level / 100.0); midori_view_set_zoom_level (view, zoom_level / 100.0);
} }
GtkWidget*
statusbar_features_property_proxy (MidoriWebSettings* settings,
const gchar* property,
GtkWidget* toolbar)
{
const gchar* kind = NULL;
GtkWidget* button;
GtkWidget* image;
if (!strcmp (property, "auto-load-images")
|| !strcmp (property, "enable-javascript")
|| !strcmp (property, "enable-plugins"))
kind = "toggle";
else if (!strcmp (property, "identify-as"))
kind = "custom-user-agent";
else if (strstr (property, "font") != NULL)
kind = "font";
else if (!strcmp (property, "zoom-level"))
{
MidoriBrowser* browser = midori_browser_get_for_widget (toolbar);
gint i;
button = gtk_combo_box_text_new_with_entry ();
gtk_entry_set_width_chars (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (button))), 4);
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (button), zoom_levels[i].label);
g_signal_connect (button, "changed",
G_CALLBACK (statusbar_features_zoom_level_changed_cb), browser);
g_signal_connect (browser, "notify::tab",
G_CALLBACK (statusbar_features_browser_notify_tab_cb), button);
statusbar_features_browser_notify_tab_cb (browser, NULL, button);
return button;
}
button = katze_property_proxy (settings, property, kind);
if (GTK_IS_BIN (button))
{
GtkWidget* label = gtk_bin_get_child (GTK_BIN (button));
if (GTK_IS_LABEL (label))
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
}
if (!strcmp (property, "auto-load-images"))
{
g_object_set_data (G_OBJECT (button), "feature-label", _("Images"));
image = gtk_image_new_from_stock (STOCK_IMAGE, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Load images automatically"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
}
if (!strcmp (property, "enable-javascript"))
{
g_object_set_data (G_OBJECT (button), "feature-label", _("Scripts"));
image = gtk_image_new_from_stock (STOCK_SCRIPT, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Enable scripts"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
}
else if (!strcmp (property, "enable-plugins"))
{
if (!midori_web_settings_has_plugin_support ())
gtk_widget_hide (button);
g_object_set_data (G_OBJECT (button), "feature-label", _("Netscape plugins"));
image = gtk_image_new_from_stock (MIDORI_STOCK_PLUGINS, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Enable Netscape plugins"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
}
return button;
}
static void static void
statusbar_features_app_add_browser_cb (MidoriApp* app, statusbar_features_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser, MidoriBrowser* browser,
@ -114,8 +192,8 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
MidoriWebSettings* settings; MidoriWebSettings* settings;
GtkWidget* toolbar; GtkWidget* toolbar;
GtkWidget* button; GtkWidget* button;
GtkWidget* image;
gsize i; gsize i;
gchar** filters;
/* FIXME: Monitor each view and modify its settings individually /* FIXME: Monitor each view and modify its settings individually
instead of merely replicating the global preferences. */ instead of merely replicating the global preferences. */
@ -124,48 +202,37 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
bbox = gtk_hbox_new (FALSE, 0); bbox = gtk_hbox_new (FALSE, 0);
settings = midori_browser_get_settings (browser); settings = midori_browser_get_settings (browser);
toolbar = katze_object_get_object (browser, "navigationbar"); toolbar = katze_object_get_object (browser, "navigationbar");
button = katze_property_proxy (settings, "auto-load-images", "toggle");
g_object_set_data (G_OBJECT (button), "feature-label", _("Images"));
image = gtk_image_new_from_stock (STOCK_IMAGE, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Load images automatically"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = katze_property_proxy (settings, "enable-scripts", "toggle");
g_object_set_data (G_OBJECT (button), "feature-label", _("Scripts"));
image = gtk_image_new_from_stock (STOCK_SCRIPTS, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Enable scripts"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = katze_property_proxy (settings, "enable-plugins", "toggle");
g_object_set_data (G_OBJECT (button), "feature-label", _("Netscape plugins"));
image = gtk_image_new_from_stock (STOCK_PLUGINS, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (button), image);
gtk_widget_set_tooltip_text (button, _("Enable Netscape plugins"));
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = katze_property_proxy (settings, "identify-as", "custom-user-agent");
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = gtk_combo_box_text_new_with_entry ();
gtk_entry_set_width_chars (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (button))), 4);
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (button), zoom_levels[i].label);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
g_signal_connect (button, "changed",
G_CALLBACK (statusbar_features_zoom_level_changed_cb), browser);
g_signal_connect (browser, "notify::tab",
G_CALLBACK (statusbar_features_browser_notify_tab_cb), button);
gtk_widget_show_all (bbox);
gtk_box_pack_start (GTK_BOX (statusbar), bbox, FALSE, FALSE, 3);
g_object_unref (statusbar);
filters = midori_extension_get_string_list (extension, "items", NULL);
if (filters && *filters)
{
i = 0;
while (filters[i] != NULL)
{
button = statusbar_features_property_proxy (settings, filters[i], toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
i++;
}
}
else
{
button = statusbar_features_property_proxy (settings, "auto-load-images", toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = statusbar_features_property_proxy (settings, "enable-javascript", toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = statusbar_features_property_proxy (settings, "enable-plugins", toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = statusbar_features_property_proxy (settings, "identify-as", toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = statusbar_features_property_proxy (settings, "zoom-level", toolbar);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
}
gtk_widget_show_all (bbox);
gtk_box_pack_end (GTK_BOX (statusbar), bbox, FALSE, FALSE, 3);
g_object_unref (statusbar);
g_object_unref (toolbar);
g_strfreev (filters);
g_signal_connect (extension, "deactivate", g_signal_connect (extension, "deactivate",
G_CALLBACK (statusbar_features_deactivate_cb), bbox); G_CALLBACK (statusbar_features_deactivate_cb), bbox);
} }
@ -194,6 +261,7 @@ extension_init (void)
"version", "0.1" MIDORI_VERSION_SUFFIX, "version", "0.1" MIDORI_VERSION_SUFFIX,
"authors", "Christian Dywan <christian@twotoasts.de>", "authors", "Christian Dywan <christian@twotoasts.de>",
NULL); NULL);
midori_extension_install_string_list (extension, "items", NULL, G_MAXSIZE);
g_signal_connect (extension, "activate", g_signal_connect (extension, "activate",
G_CALLBACK (statusbar_features_activate_cb), NULL); G_CALLBACK (statusbar_features_activate_cb), NULL);

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2008-2009 Christian Dywan <christian@twotoasts.de> Copyright (C) 2008-2013 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -39,12 +39,11 @@ tab_panel_browser_notify_tab_cb (MidoriBrowser* browser,
GtkTreeView* treeview); GtkTreeView* treeview);
static void static void
tab_panel_browser_move_tab_cb (MidoriBrowser* browser, tab_panel_browser_move_tab_cb (MidoriBrowser* browser,
GtkNotebook* notebook, GtkWidget* notebook,
gint cur_pos, gint cur_pos,
gint new_pos, gint new_pos,
gpointer user_data); gpointer user_data);
static void static void
tab_panel_view_notify_minimized_cb (GtkWidget* view, tab_panel_view_notify_minimized_cb (GtkWidget* view,
GParamSpec* pspec, GParamSpec* pspec,
@ -104,13 +103,19 @@ tab_panel_deactivate_cb (MidoriExtension* extension,
GtkWidget* treeview) GtkWidget* treeview)
{ {
MidoriApp* app = midori_extension_get_app (extension); MidoriApp* app = midori_extension_get_app (extension);
GtkTreeModel* model; MidoriBrowser* browser = midori_browser_get_for_widget (treeview);
MidoriBrowser* browser; GtkTreeModel* model = tab_panel_get_model_for_browser (browser);
GList* tabs = midori_browser_get_tabs (browser);
browser = midori_browser_get_for_widget (treeview); for (; tabs; tabs = g_list_next (tabs))
g_object_set (browser, "show-tabs", TRUE, NULL); {
model = tab_panel_get_model_for_browser (browser); g_signal_handlers_disconnect_by_func (
g_object_unref (model); tabs->data, tab_panel_view_notify_minimized_cb, extension);
g_signal_handlers_disconnect_by_func (
tabs->data, tab_panel_view_notify_icon_cb, extension);
g_signal_handlers_disconnect_by_func (
tabs->data, tab_panel_view_notify_title_cb, extension);
}
g_list_free (tabs);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
extension, tab_panel_deactivate_cb, treeview); extension, tab_panel_deactivate_cb, treeview);
@ -124,16 +129,14 @@ tab_panel_deactivate_cb (MidoriExtension* extension,
browser, tab_panel_browser_notify_tab_cb, treeview); browser, tab_panel_browser_notify_tab_cb, treeview);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
browser, tab_panel_settings_notify_cb, model); browser, tab_panel_settings_notify_cb, model);
g_signal_handlers_disconnect_by_func (
browser, tab_panel_view_notify_minimized_cb, extension);
g_signal_handlers_disconnect_by_func (
browser, tab_panel_view_notify_icon_cb, extension);
g_signal_handlers_disconnect_by_func (
browser, tab_panel_view_notify_title_cb, extension);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
browser, tab_panel_browser_move_tab_cb, NULL); browser, tab_panel_browser_move_tab_cb, NULL);
gtk_widget_destroy (treeview); gtk_widget_destroy (treeview);
g_object_unref (model);
g_object_set_data (G_OBJECT (browser), "tab-panel-ext-model", NULL);
g_object_set (browser, "show-tabs", TRUE, NULL);
} }
static void static void
@ -143,7 +146,6 @@ midori_extension_cursor_or_row_changed_cb (GtkTreeView* treeview,
/* Nothing to do */ /* Nothing to do */
} }
#if GTK_CHECK_VERSION (2, 12, 0)
static gboolean static gboolean
tab_panel_treeview_query_tooltip_cb (GtkWidget* treeview, tab_panel_treeview_query_tooltip_cb (GtkWidget* treeview,
gint x, gint x,
@ -171,7 +173,6 @@ tab_panel_treeview_query_tooltip_cb (GtkWidget* treeview,
return TRUE; return TRUE;
} }
#endif
static void static void
midori_extension_row_activated_cb (GtkTreeView* treeview, midori_extension_row_activated_cb (GtkTreeView* treeview,
@ -231,12 +232,12 @@ midori_extension_button_release_event_cb (GtkWidget* widget,
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
event->x, event->y, NULL, &column, NULL, NULL) event->x, event->y, NULL, &column, NULL, NULL)
&& column == gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 1)) && column == gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 1))
gtk_widget_destroy (view); midori_browser_close_tab (browser, view);
else else
midori_browser_set_current_tab (browser, view); midori_browser_set_current_tab (browser, view);
} }
else if (event->button == 2) else if (event->button == 2)
gtk_widget_destroy (view); midori_browser_close_tab (midori_browser_get_for_widget (widget), view);
else else
tab_panel_popup (widget, event, view); tab_panel_popup (widget, event, view);
@ -353,13 +354,14 @@ tab_panel_view_notify_icon_cb (GtkWidget* view,
{ {
GtkTreeModel* model = tab_panel_get_model_for_browser (browser); GtkTreeModel* model = tab_panel_get_model_for_browser (browser);
GtkTreeIter iter; GtkTreeIter iter;
GtkWidget* label = midori_view_get_proxy_tab_label (MIDORI_VIEW (view)); GdkColor* fg = midori_tab_get_fg_color (MIDORI_TAB (view));
GtkStyle* style = gtk_widget_get_style (label); GdkColor* bg = midori_tab_get_bg_color (MIDORI_TAB (view));
if (tab_panel_get_iter_for_view (model, &iter, view)) if (tab_panel_get_iter_for_view (model, &iter, view))
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
3, icon, 3, icon,
6, &style->bg[GTK_STATE_NORMAL], 6, bg,
7, &style->fg[GTK_STATE_NORMAL], 7, fg,
-1); -1);
} }
} }
@ -382,11 +384,15 @@ tab_panel_view_notify_title_cb (GtkWidget* view,
{ {
GtkTreeModel* model = tab_panel_get_model_for_browser (browser); GtkTreeModel* model = tab_panel_get_model_for_browser (browser);
GtkTreeIter iter; GtkTreeIter iter;
GdkColor* fg = midori_tab_get_fg_color (MIDORI_TAB (view));
GdkColor* bg = midori_tab_get_bg_color (MIDORI_TAB (view));
if (tab_panel_get_iter_for_view (model, &iter, view)) if (tab_panel_get_iter_for_view (model, &iter, view))
{ {
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
4, title, 4, title,
5, midori_view_get_label_ellipsize (MIDORI_VIEW (view)), 5, midori_view_get_label_ellipsize (MIDORI_VIEW (view)),
6, bg,
7, fg,
-1); -1);
} }
} }
@ -419,8 +425,7 @@ tab_panel_browser_add_tab_cb (MidoriBrowser* browser,
GtkWidget* view, GtkWidget* view,
MidoriExtension* extension) MidoriExtension* extension)
{ {
GtkWidget* notebook = katze_object_get_object (browser, "notebook"); gint page = midori_browser_page_num (browser, view);
gint page = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), view);
MidoriWebSettings* settings = midori_browser_get_settings (browser); MidoriWebSettings* settings = midori_browser_get_settings (browser);
gboolean minimized = katze_object_get_boolean (view, "minimized"); gboolean minimized = katze_object_get_boolean (view, "minimized");
GdkPixbuf* icon = midori_view_get_icon (MIDORI_VIEW (view)); GdkPixbuf* icon = midori_view_get_icon (MIDORI_VIEW (view));
@ -449,10 +454,12 @@ tab_panel_browser_add_tab_cb (MidoriBrowser* browser,
GtkTreeIter iter; GtkTreeIter iter;
gboolean buttons = katze_object_get_boolean (settings, "close-buttons-on-tabs"); gboolean buttons = katze_object_get_boolean (settings, "close-buttons-on-tabs");
gint ellipsize = midori_view_get_label_ellipsize (MIDORI_VIEW (view)); gint ellipsize = midori_view_get_label_ellipsize (MIDORI_VIEW (view));
GdkColor* fg = midori_tab_get_fg_color (MIDORI_TAB (view));
GdkColor* bg = midori_tab_get_bg_color (MIDORI_TAB (view));
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), gtk_tree_store_insert_with_values (GTK_TREE_STORE (model),
&iter, NULL, page, 0, view, 1, GTK_STOCK_CLOSE, 2, buttons, &iter, NULL, page, 0, view, 1, GTK_STOCK_CLOSE, 2, buttons,
3, icon, 4, title, 5, ellipsize, 6, NULL, 7, NULL, -1); 3, icon, 4, title, 5, ellipsize, 6, bg, 7, fg, -1);
} }
if (!g_signal_handler_find (view, G_SIGNAL_MATCH_FUNC, if (!g_signal_handler_find (view, G_SIGNAL_MATCH_FUNC,
@ -468,16 +475,6 @@ tab_panel_browser_add_tab_cb (MidoriBrowser* browser,
g_signal_connect (view, "notify::title", g_signal_connect (view, "notify::title",
G_CALLBACK (tab_panel_view_notify_title_cb), extension); G_CALLBACK (tab_panel_view_notify_title_cb), extension);
} }
g_object_unref (notebook);
}
static void
tab_panel_browser_foreach_cb (GtkWidget* view,
MidoriExtension* extension)
{
tab_panel_browser_add_tab_cb (midori_browser_get_for_widget (view),
view, extension);
} }
static void static void
@ -537,11 +534,9 @@ tab_panel_app_add_browser_cb (MidoriApp* app,
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model)); treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (treeview), FALSE); gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (treeview), FALSE);
#if GTK_CHECK_VERSION (2, 12, 0)
g_signal_connect (treeview, "query-tooltip", g_signal_connect (treeview, "query-tooltip",
G_CALLBACK (tab_panel_treeview_query_tooltip_cb), NULL); G_CALLBACK (tab_panel_treeview_query_tooltip_cb), NULL);
gtk_widget_set_has_tooltip (treeview, TRUE); gtk_widget_set_has_tooltip (treeview, TRUE);
#endif
column = gtk_tree_view_column_new (); column = gtk_tree_view_column_new ();
renderer_pixbuf = gtk_cell_renderer_pixbuf_new (); renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE); gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
@ -598,8 +593,10 @@ tab_panel_app_add_browser_cb (MidoriApp* app,
midori_panel_set_current_page (MIDORI_PANEL (panel), i); midori_panel_set_current_page (MIDORI_PANEL (panel), i);
g_object_unref (panel); g_object_unref (panel);
midori_browser_foreach (browser, GList* tabs = midori_browser_get_tabs (browser);
(GtkCallback)tab_panel_browser_foreach_cb, treeview); for (; tabs; tabs = g_list_next (tabs))
tab_panel_browser_add_tab_cb (browser, tabs->data, extension);
g_list_free (tabs);
g_signal_connect_after (browser, "add-tab", g_signal_connect_after (browser, "add-tab",
G_CALLBACK (tab_panel_browser_add_tab_cb), extension); G_CALLBACK (tab_panel_browser_add_tab_cb), extension);
@ -630,7 +627,7 @@ tab_panel_activate_cb (MidoriExtension* extension,
static void static void
tab_panel_browser_move_tab_cb (MidoriBrowser* browser, tab_panel_browser_move_tab_cb (MidoriBrowser* browser,
GtkNotebook* notebook, GtkWidget* notebook,
gint cur_pos, gint cur_pos,
gint new_pos, gint new_pos,
gpointer user_data) gpointer user_data)
@ -639,7 +636,7 @@ tab_panel_browser_move_tab_cb (MidoriBrowser* browser,
gint last_page; gint last_page;
GtkTreeModel *model; GtkTreeModel *model;
last_page = gtk_notebook_get_n_pages (notebook) - 1; last_page = midori_browser_get_n_pages (browser) - 1;
model = tab_panel_get_model_for_browser (browser); model = tab_panel_get_model_for_browser (browser);
gtk_tree_model_iter_nth_child (model, &cur, NULL, cur_pos); gtk_tree_model_iter_nth_child (model, &cur, NULL, cur_pos);

View file

@ -46,8 +46,6 @@ static const GtkTargetEntry tb_editor_dnd_targets[] =
}; };
static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets); static const gint tb_editor_dnd_targets_len = G_N_ELEMENTS(tb_editor_dnd_targets);
static void tb_editor_browser_populate_tool_menu_cb(MidoriBrowser *browser, GtkWidget *menu, MidoriExtension *ext);
static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, GtkWidget *menu, static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, GtkWidget *menu,
MidoriExtension *ext); MidoriExtension *ext);
@ -58,7 +56,6 @@ static void tb_editor_deactivate_cb(MidoriExtension *extension, MidoriBrowser *b
{ {
MidoriApp *app = midori_extension_get_app(extension); MidoriApp *app = midori_extension_get_app(extension);
g_signal_handlers_disconnect_by_func(browser, tb_editor_browser_populate_tool_menu_cb, extension);
g_signal_handlers_disconnect_by_func(browser, tb_editor_browser_populate_toolbar_menu_cb, extension); g_signal_handlers_disconnect_by_func(browser, tb_editor_browser_populate_toolbar_menu_cb, extension);
g_signal_handlers_disconnect_by_func(extension, tb_editor_deactivate_cb, browser); g_signal_handlers_disconnect_by_func(extension, tb_editor_deactivate_cb, browser);
g_signal_handlers_disconnect_by_func(app, tb_editor_app_add_browser_cb, extension); g_signal_handlers_disconnect_by_func(app, tb_editor_app_add_browser_cb, extension);
@ -107,7 +104,12 @@ static GSList *tb_editor_array_to_list(const gchar **items)
name = items; name = items;
while (*name != NULL) while (*name != NULL)
{ {
#ifdef HAVE_GRANITE
/* A "new tab" button is already part of the notebook */
if (*name[0] != '\0' && strcmp (*name, "TabNew"))
#else
if (*name[0] != '\0') if (*name[0] != '\0')
#endif
list = g_slist_append(list, g_strdup(*name)); list = g_slist_append(list, g_strdup(*name));
name++; name++;
} }
@ -274,16 +276,15 @@ static void tb_editor_drag_data_rcvd_cb(GtkWidget *widget, GdkDragContext *conte
gint x, gint y, GtkSelectionData *data, guint info, gint x, gint y, GtkSelectionData *data, guint info,
guint ltime, TBEditorWidget *tbw) guint ltime, TBEditorWidget *tbw)
{ {
#if !GTK_CHECK_VERSION(3,0,0) /* TODO */
GtkTreeView *tree = GTK_TREE_VIEW(widget); GtkTreeView *tree = GTK_TREE_VIEW(widget);
gboolean del = FALSE; gboolean del = FALSE;
if (data->length >= 0 && data->format == 8) if (gtk_selection_data_get_length (data) >= 0 && gtk_selection_data_get_format (data) == 8)
{ {
gboolean is_sep; gboolean is_sep;
gchar *text = NULL; gchar *text = NULL;
text = (gchar*) data->data; text = (gchar*) gtk_selection_data_get_data (data);
/* We allow re-ordering the Location item but not removing it from the list. */ /* We allow re-ordering the Location item but not removing it from the list. */
if (g_strcmp0(text, "Location") == 0 && widget != tbw->drag_source) if (g_strcmp0(text, "Location") == 0 && widget != tbw->drag_source)
@ -332,7 +333,6 @@ static void tb_editor_drag_data_rcvd_cb(GtkWidget *widget, GdkDragContext *conte
tbw->drag_source = NULL; /* reset the value just to be sure */ tbw->drag_source = NULL; /* reset the value just to be sure */
tb_editor_free_path(tbw); tb_editor_free_path(tbw);
gtk_drag_finish(context, TRUE, del, ltime); gtk_drag_finish(context, TRUE, del, ltime);
#endif
} }
@ -394,11 +394,7 @@ static TBEditorWidget *tb_editor_create_dialog(MidoriBrowser *parent)
GTK_WINDOW(parent), GTK_WINDOW(parent),
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
#if !GTK_CHECK_VERSION(3,0,0) vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
vbox = (GTK_DIALOG(dialog))->vbox;
#else
vbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
#endif
gtk_box_set_spacing(GTK_BOX(vbox), 6); gtk_box_set_spacing(GTK_BOX(vbox), 6);
gtk_widget_set_name(dialog, "GeanyDialog"); gtk_widget_set_name(dialog, "GeanyDialog");
gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400); gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);
@ -410,7 +406,7 @@ static TBEditorWidget *tb_editor_create_dialog(MidoriBrowser *parent)
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
label = gtk_label_new( label = gtk_label_new(
_("Select items to be displayed on the toolbar. Items can be reodered by drag and drop.")); _("Select items to be displayed on the toolbar. Items can be reordered by drag and drop."));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
tree_available = gtk_tree_view_new(); tree_available = gtk_tree_view_new();
@ -578,17 +574,6 @@ static void tb_editor_menu_configure_toolbar_activate_cb(GtkWidget *menuitem, Mi
g_free(tbw); g_free(tbw);
} }
static void tb_editor_browser_populate_tool_menu_cb(MidoriBrowser *browser, GtkWidget *menu, MidoriExtension *ext)
{
GtkWidget *menuitem;
menuitem = gtk_menu_item_new_with_mnemonic (_("Customize _Toolbar..."));
g_signal_connect (menuitem, "activate",
G_CALLBACK (tb_editor_menu_configure_toolbar_activate_cb), browser);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, GtkWidget *menu, static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, GtkWidget *menu,
MidoriExtension *ext) MidoriExtension *ext)
{ {
@ -598,7 +583,7 @@ static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, G
separator = gtk_separator_menu_item_new (); separator = gtk_separator_menu_item_new ();
gtk_widget_show (separator); gtk_widget_show (separator);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator); gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
menuitem = gtk_menu_item_new_with_mnemonic (_("_Customize...")); menuitem = gtk_menu_item_new_with_mnemonic (_("_Customize Toolbar…"));
g_signal_connect (menuitem, "activate", g_signal_connect (menuitem, "activate",
G_CALLBACK (tb_editor_menu_configure_toolbar_activate_cb), browser); G_CALLBACK (tb_editor_menu_configure_toolbar_activate_cb), browser);
gtk_widget_show (menuitem); gtk_widget_show (menuitem);
@ -607,7 +592,6 @@ static void tb_editor_browser_populate_toolbar_menu_cb(MidoriBrowser *browser, G
static void tb_editor_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext) static void tb_editor_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext)
{ {
g_signal_connect(browser, "populate-tool-menu", G_CALLBACK(tb_editor_browser_populate_tool_menu_cb), ext);
g_signal_connect(browser, "populate-toolbar-menu", G_CALLBACK(tb_editor_browser_populate_toolbar_menu_cb), ext); g_signal_connect(browser, "populate-toolbar-menu", G_CALLBACK(tb_editor_browser_populate_toolbar_menu_cb), ext);
g_signal_connect(ext, "deactivate", G_CALLBACK(tb_editor_deactivate_cb), browser); g_signal_connect(ext, "deactivate", G_CALLBACK(tb_editor_deactivate_cb), browser);
} }

530
extensions/transfers.vala Normal file
View file

@ -0,0 +1,530 @@
/*
Copyright (C) 2009-2013 Christian Dywan <christian@twotoasts.de>
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 Gtk {
extern static void widget_size_request (Gtk.Widget widget, out Gtk.Requisition requisition);
}
namespace Sokoke {
extern static bool show_uri (Gdk.Screen screen, string uri, uint32 timestamp) throws Error;
extern static void widget_get_text_size (Gtk.Widget widget, string sample, out int width, out int height);
}
namespace Transfers {
private class Transfer : GLib.Object {
internal WebKit.Download download;
internal signal void changed ();
internal signal void remove ();
internal signal void removed ();
internal int action { get {
return Midori.Download.get_type (download);
} }
internal double progress { get {
return Midori.Download.get_progress (download);
} }
#if HAVE_WEBKIT2
public bool succeeded { get; protected set; default = false; }
public bool finished { get; protected set; default = false; }
internal string destination { get {
return download.destination;
} }
#else
internal bool succeeded { get {
return download.status == WebKit.DownloadStatus.FINISHED;
} }
internal bool finished { get {
return Midori.Download.is_finished (download);
} }
internal string destination { get {
return download.destination_uri;
} }
#endif
internal Transfer (WebKit.Download download) {
this.download = download;
#if HAVE_WEBKIT2
download.notify["estimated-progress"].connect (transfer_changed);
download.finished.connect (() => {
succeeded = finished = true;
changed ();
});
download.failed.connect (() => {
succeeded = false;
finished = true;
changed ();
});
#else
download.notify["status"].connect (transfer_changed);
download.notify["progress"].connect (transfer_changed);
#endif
}
void transfer_changed (GLib.ParamSpec pspec) {
changed ();
}
}
static bool pending_transfers (Katze.Array array) {
foreach (GLib.Object item in array.get_items ()) {
var transfer = item as Transfer;
if (!transfer.finished)
return true;
}
return false;
}
private class Sidebar : Gtk.VBox, Midori.Viewable {
Gtk.Toolbar? toolbar = null;
Gtk.ToolButton clear;
Gtk.ListStore store = new Gtk.ListStore (1, typeof (Transfer));
Gtk.TreeView treeview;
Katze.Array array;
public unowned string get_stock_id () {
return Midori.Stock.TRANSFER;
}
public unowned string get_label () {
return _("Transfers");
}
public Gtk.Widget get_toolbar () {
if (toolbar == null) {
toolbar = new Gtk.Toolbar ();
toolbar.set_icon_size (Gtk.IconSize.BUTTON);
toolbar.insert (new Gtk.ToolItem (), -1);
var separator = new Gtk.SeparatorToolItem ();
separator.draw = false;
separator.set_expand (true);
toolbar.insert (separator, -1);
clear = new Gtk.ToolButton.from_stock (Gtk.STOCK_CLEAR);
clear.label = _("Clear All");
clear.is_important = true;
clear.clicked.connect (clear_clicked);
clear.sensitive = !array.is_empty ();
toolbar.insert (clear, -1);
toolbar.show_all ();
}
return toolbar;
}
void clear_clicked () {
foreach (GLib.Object item in array.get_items ()) {
var transfer = item as Transfer;
if (transfer.finished)
transfer.remove ();
}
}
public Sidebar (Katze.Array array) {
Gtk.TreeViewColumn column;
treeview = new Gtk.TreeView.with_model (store);
treeview.headers_visible = false;
store.set_sort_column_id (0, Gtk.SortType.ASCENDING);
store.set_sort_func (0, tree_sort_func);
column = new Gtk.TreeViewColumn ();
Gtk.CellRendererPixbuf renderer_icon = new Gtk.CellRendererPixbuf ();
column.pack_start (renderer_icon, false);
column.set_cell_data_func (renderer_icon, on_render_icon);
treeview.append_column (column);
column = new Gtk.TreeViewColumn ();
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
Gtk.CellRendererProgress renderer_progress = new Gtk.CellRendererProgress ();
column.pack_start (renderer_progress, true);
column.set_expand (true);
column.set_cell_data_func (renderer_progress, on_render_text);
treeview.append_column (column);
column = new Gtk.TreeViewColumn ();
Gtk.CellRendererPixbuf renderer_button = new Gtk.CellRendererPixbuf ();
column.pack_start (renderer_button, false);
column.set_cell_data_func (renderer_button, on_render_button);
treeview.append_column (column);
treeview.row_activated.connect (row_activated);
treeview.button_release_event.connect (button_released);
treeview.popup_menu.connect (menu_popup);
treeview.show ();
pack_start (treeview, true, true, 0);
this.array = array;
array.add_item.connect (transfer_added);
array.remove_item.connect_after (transfer_removed);
foreach (GLib.Object item in array.get_items ())
transfer_added (item);
}
void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
Gtk.TreeIter iter;
if (store.get_iter (out iter, path)) {
Transfer transfer;
store.get (iter, 0, out transfer);
if (Midori.Download.action_clear (transfer.download, treeview))
transfer.remove ();
}
}
bool button_released (Gdk.EventButton event) {
if (event.button == 3)
return show_popup_menu (event);
return false;
}
bool menu_popup () {
return show_popup_menu (null);
}
bool show_popup_menu (Gdk.EventButton? event) {
Gtk.TreeIter iter;
if (treeview.get_selection ().get_selected (null, out iter)) {
Transfer transfer;
store.get (iter, 0, out transfer);
var menu = new Gtk.Menu ();
var menuitem = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_OPEN, null);
menuitem.activate.connect (() => {
Midori.Download.open (transfer.download, treeview);
});
menuitem.sensitive = transfer.succeeded;
menu.append (menuitem);
menuitem = new Gtk.ImageMenuItem.with_mnemonic (_("Open Destination _Folder"));
menuitem.image = new Gtk.Image.from_stock (Gtk.STOCK_DIRECTORY, Gtk.IconSize.MENU);
menuitem.activate.connect (() => {
var folder = GLib.File.new_for_uri (transfer.destination);
Sokoke.show_uri (get_screen (), folder.get_parent ().get_uri (), 0);
});
menu.append (menuitem);
menuitem = new Gtk.ImageMenuItem.with_mnemonic (_("Copy Link Loc_ation"));
menuitem.activate.connect (() => {
string uri = transfer.destination;
get_clipboard (Gdk.SELECTION_PRIMARY).set_text (uri, -1);
get_clipboard (Gdk.SELECTION_CLIPBOARD).set_text (uri, -1);
});
menuitem.image = new Gtk.Image.from_stock (Gtk.STOCK_COPY, Gtk.IconSize.MENU);
menu.append (menuitem);
menu.show_all ();
Katze.widget_popup (treeview, menu, null, Katze.MenuPos.CURSOR);
return true;
}
return false;
}
int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
Transfer transfer1, transfer2;
model.get (a, 0, out transfer1);
model.get (b, 0, out transfer2);
return (transfer1.finished ? 1 : 0) - (transfer2.finished ? 1 : 0);
}
void transfer_changed () {
treeview.queue_draw ();
}
void transfer_added (GLib.Object item) {
var transfer = item as Transfer;
Gtk.TreeIter iter;
store.append (out iter);
store.set (iter, 0, transfer);
transfer.changed.connect (transfer_changed);
clear.sensitive = true;
}
void transfer_removed (GLib.Object item) {
var transfer = item as Transfer;
transfer.changed.disconnect (transfer_changed);
Gtk.TreeIter iter;
if (store.iter_children (out iter, null)) {
do {
Transfer found;
store.get (iter, 0, out found);
if (transfer == found) {
store.remove (iter);
break;
}
} while (store.iter_next (ref iter));
}
if (array.is_empty ())
clear.sensitive = false;
}
void on_render_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
Gtk.TreeModel model, Gtk.TreeIter iter) {
Transfer transfer;
model.get (iter, 0, out transfer);
string content_type = Midori.Download.get_content_type (transfer.download, null);
var icon = GLib.ContentType.get_icon (content_type) as ThemedIcon;
icon.append_name ("text-html");
renderer.set ("gicon", icon,
"stock-size", Gtk.IconSize.DND,
"xpad", 1, "ypad", 12);
}
void on_render_text (Gtk.CellLayout column, Gtk.CellRenderer renderer,
Gtk.TreeModel model, Gtk.TreeIter iter) {
Transfer transfer;
model.get (iter, 0, out transfer);
string tooltip = Midori.Download.get_tooltip (transfer.download);
renderer.set ("text", tooltip,
"value", (int)(transfer.progress * 100));
}
void on_render_button (Gtk.CellLayout column, Gtk.CellRenderer renderer,
Gtk.TreeModel model, Gtk.TreeIter iter) {
Transfer transfer;
model.get (iter, 0, out transfer);
string stock_id = Midori.Download.action_stock_id (transfer.download);
renderer.set ("stock-id", stock_id,
"stock-size", Gtk.IconSize.MENU);
}
}
private class TransferButton : Gtk.ToolItem {
Transfer transfer;
Gtk.ProgressBar progress;
Gtk.Image icon;
Gtk.Button button;
public TransferButton (Transfer transfer) {
this.transfer = transfer;
var box = new Gtk.HBox (false, 0);
progress = new Gtk.ProgressBar ();
#if HAVE_GTK3
progress.show_text = true;
#endif
progress.ellipsize = Pango.EllipsizeMode.MIDDLE;
string filename = Path.get_basename (transfer.destination);
progress.text = filename;
int width;
Sokoke.widget_get_text_size (progress, "M", out width, null);
progress.set_size_request (width * 10, 1);
box.pack_start (progress, false, false, 0);
icon = new Gtk.Image ();
button = new Gtk.Button ();
button.relief = Gtk.ReliefStyle.NONE;
button.focus_on_click = false;
button.clicked.connect (button_clicked);
button.add (icon);
box.pack_start (button, false, false, 0);
add (box);
show_all ();
transfer.changed.connect (transfer_changed);
transfer_changed ();
transfer.removed.connect (transfer_removed);
}
void button_clicked () {
if (Midori.Download.action_clear (transfer.download, button))
transfer.remove ();
}
void transfer_changed () {
progress.fraction = Midori.Download.get_progress (transfer.download);
progress.tooltip_text = Midori.Download.get_tooltip (transfer.download);
string stock_id = Midori.Download.action_stock_id (transfer.download);
icon.set_from_stock (stock_id, Gtk.IconSize.MENU);
}
void transfer_removed () {
destroy ();
}
}
private class Toolbar : Gtk.Toolbar {
Katze.Array array;
Gtk.ToolButton clear;
void clear_clicked () {
foreach (GLib.Object item in array.get_items ()) {
var transfer = item as Transfer;
if (transfer.finished)
array.remove_item (item);
}
}
public Toolbar (Katze.Array array) {
set_icon_size (Gtk.IconSize.BUTTON);
set_style (Gtk.ToolbarStyle.BOTH_HORIZ);
show_arrow = false;
clear = new Gtk.ToolButton.from_stock (Gtk.STOCK_CLEAR);
clear.label = _("Clear All");
clear.is_important = true;
clear.clicked.connect (clear_clicked);
clear.sensitive = !array.is_empty ();
insert (clear, -1);
clear.show ();
clear.sensitive = false;
this.array = array;
array.add_item.connect (transfer_added);
array.remove_item.connect_after (transfer_removed);
foreach (GLib.Object item in array.get_items ())
transfer_added (item);
}
void transfer_added (GLib.Object item) {
var transfer = item as Transfer;
/* Newest item on the left */
insert (new TransferButton (transfer), 0);
clear.sensitive = true;
show ();
Gtk.Requisition req;
Gtk.widget_size_request (parent, out req);
int reqwidth = req.width;
int winwidth;
(get_toplevel () as Gtk.Window).get_size (out winwidth, null);
if (reqwidth > winwidth)
clear_clicked ();
}
void transfer_removed (GLib.Object item) {
clear.sensitive = pending_transfers (array);
if (array.is_empty ())
hide ();
}
}
private class Manager : Midori.Extension {
internal Katze.Array array;
internal GLib.List<Gtk.Widget> widgets;
void download_added (WebKit.Download download) {
var transfer = new Transfer (download);
transfer.remove.connect (transfer_remove);
transfer.changed.connect (transfer_changed);
array.remove_item.connect (transfer_removed);
array.add_item (transfer);
}
void transfer_changed (Transfer transfer) {
if (transfer.succeeded) {
/* FIXME: The following 2 blocks ought to be done in core */
if (transfer.action == Midori.DownloadType.OPEN) {
if (Midori.Download.action_clear (transfer.download, widgets.nth_data (0)))
transfer.remove ();
}
string uri = transfer.destination;
string filename = Path.get_basename (uri);
var item = new Katze.Item ();
item.uri = uri;
item.name = filename;
Midori.Browser.update_history (item, "download", "create");
if (!Midori.Download.has_wrong_checksum (transfer.download))
Gtk.RecentManager.get_default ().add_item (uri);
string msg = _("The file '<b>%s</b>' has been downloaded.").printf (filename);
get_app ().send_notification (_("Transfer completed"), msg);
}
}
void transfer_remove (Transfer transfer) {
array.remove_item (transfer);
}
void transfer_removed (GLib.Object item) {
var transfer = item as Transfer;
transfer.removed ();
}
#if HAVE_GTK3
bool browser_closed (Gtk.Widget widget, Gdk.EventAny event) {
#else
bool browser_closed (Gtk.Widget widget, Gdk.Event event) {
#endif
var browser = widget as Midori.Browser;
if (pending_transfers (array)) {
var dialog = new Gtk.MessageDialog (browser,
Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.WARNING, Gtk.ButtonsType.NONE,
_("Some files are being downloaded"));
dialog.title = _("Some files are being downloaded");
dialog.add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
_("_Quit Midori"), Gtk.ResponseType.ACCEPT);
dialog.format_secondary_text (
_("The transfers will be cancelled if Midori quits."));
bool cancel = dialog.run () != Gtk.ResponseType.ACCEPT;
dialog.destroy ();
return cancel;
}
return false;
}
void browser_added (Midori.Browser browser) {
var viewable = new Sidebar (array);
viewable.show ();
browser.panel.append_page (viewable);
widgets.append (viewable);
var toolbar = new Toolbar (array);
#if HAVE_GTK3
browser.statusbar.pack_end (toolbar, false, false);
#else
browser.statusbar.pack_start (toolbar, false, false);
#endif
widgets.append (toolbar);
// TODO: popover
// TODO: progress in dock item
browser.add_download.connect (download_added);
browser.delete_event.connect (browser_closed);
}
void activated (Midori.App app) {
array = new Katze.Array (typeof (Transfer));
widgets = new GLib.List<Gtk.Widget> ();
foreach (var browser in app.get_browsers ())
browser_added (browser);
app.add_browser.connect (browser_added);
}
void deactivated () {
var app = get_app ();
app.add_browser.disconnect (browser_added);
foreach (var browser in app.get_browsers ()) {
browser.add_download.disconnect (download_added);
browser.delete_event.disconnect (browser_closed);
}
foreach (var widget in widgets)
widget.destroy ();
array.remove_item.disconnect (transfer_removed);
}
internal Manager () {
GLib.Object (name: _("Transfer Manager"),
description: _("View downloaded files"),
version: "0.1" + Midori.VERSION_SUFFIX,
authors: "Christian Dywan <christian@twotoasts.de>");
this.activate.connect (activated);
this.deactivate.connect (deactivated);
}
}
}
public Midori.Extension extension_init () {
return new Transfers.Manager ();
}

View file

@ -28,8 +28,7 @@ web_cache_get_cache_dir (void)
{ {
static gchar* cache_dir = NULL; static gchar* cache_dir = NULL;
if (!cache_dir) if (!cache_dir)
cache_dir = g_build_filename (g_get_user_cache_dir (), cache_dir = g_build_filename (midori_paths_get_cache_dir (), "web", NULL);
PACKAGE_NAME, "web", NULL);
return cache_dir; return cache_dir;
} }
@ -448,7 +447,7 @@ web_cache_activate_cb (MidoriExtension* extension,
static void static void
web_cache_clear_cache_cb (void) web_cache_clear_cache_cb (void)
{ {
sokoke_remove_path (web_cache_get_cache_dir (), TRUE); midori_paths_remove_path (web_cache_get_cache_dir ());
} }
#endif #endif
@ -468,7 +467,7 @@ extension_init (void)
g_signal_connect (extension, "activate", g_signal_connect (extension, "activate",
G_CALLBACK (web_cache_activate_cb), NULL); G_CALLBACK (web_cache_activate_cb), NULL);
sokoke_register_privacy_item ("web-cache", _("Web Cache"), midori_private_data_register_item ("web-cache", _("Web Cache"),
G_CALLBACK (web_cache_clear_cache_cb)); G_CALLBACK (web_cache_clear_cache_cb));
return extension; return extension;

View file

@ -2,6 +2,7 @@
# WAF build script for midori # WAF build script for midori
# This file is licensed under the terms of the expat license, see the file EXPAT. # This file is licensed under the terms of the expat license, see the file EXPAT.
import Options
import os import os
extensions = os.listdir ('extensions') extensions = os.listdir ('extensions')
@ -12,9 +13,7 @@ for extension in extensions:
target = extension target = extension
source = '' source = ''
for fila in files: for fila in files:
if fila[-2:] == '.c': if fila[-2:] == '.c' or fila[-5:] == '.vala':
source += ' ' + extension + os.sep + fila
elif 'VALAC' in bld.env and fila[-5:] == '.vala':
source += ' ' + extension + os.sep + fila source += ' ' + extension + os.sep + fila
if not source: if not source:
Utils.pprint ('RED', folder + ': No source files found') Utils.pprint ('RED', folder + ': No source files found')
@ -22,23 +21,36 @@ for extension in extensions:
else: else:
if extension[-2:] == '.c': if extension[-2:] == '.c':
target = extension[:-2] target = extension[:-2]
elif 'VALAC' in bld.env and extension[-5:] == '.vala': elif extension[-5:] == '.vala':
target = extension[:-5] target = extension[:-5]
else: else:
continue continue
source = extension source = extension
# FIXME
if bld.env['HAVE_WEBKIT2'] and target in ['external-download-manager', 'nsplugin-manager', 'formhistory', 'adblock', 'cookie-permissions', 'addons', 'cookie-manager']:
continue
obj = bld.new_task_gen ('cc', 'shlib') obj = bld.new_task_gen ('cc', 'shlib')
obj.target = target obj.target = target
obj.includes = '..' obj.includes = '.. ../katze ../midori'
obj.source = source obj.source = source
obj.uselib = 'UNIQUE LIBSOUP GIO GTK SQLITE WEBKIT LIBXML HILDON' obj.uselib = 'UNIQUE LIBSOUP GIO GTK SQLITE WEBKIT LIBXML GRANITE'
obj.vapi_dirs = '../midori' if 'vala' in source:
obj.packages = 'glib-2.0 gio-2.0 libsoup-2.4 midori' obj.env.append_value ('CCFLAGS', '-w')
obj.vapi_dirs = '../midori ../katze'
obj.packages = 'glib-2.0 gio-2.0 libsoup-2.4 sqlite3 midori midori-core katze'
if bld.env['HAVE_GTK3']: if bld.env['HAVE_GTK3']:
obj.packages += ' gtk+-3.0 webkitgtk-3.0' obj.packages += ' gtk+-3.0'
else: else:
obj.packages += ' gtk+-2.0 webkit-1.0 unique-1.0' obj.packages += ' gtk+-2.0'
if bld.env['HAVE_WEBKIT2']:
obj.packages += ' webkit2gtk-3.0'
else:
obj.packages += ' webkitgtk-3.0'
if bld.env['HAVE_GRANITE']:
obj.packages += ' granite'
obj.install_path = '${LIBDIR}/midori' obj.install_path = '${LIBDIR}/midori'
# See LINKFLAGS in wscript: w/ o it we get several "undefined reference" errors
if bld.env['platform'] == 'win32': if bld.env['platform'] == 'win32':
obj.uselib_local = 'midori' obj.uselib_local = 'midori'

View file

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 832 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -41,4 +41,4 @@ def add_image (bld, category, name):
add_image (bld, 'categories', 'extension') add_image (bld, 'categories', 'extension')
add_image (bld, 'apps', 'midori') add_image (bld, 'apps', 'midori')
add_image (bld, 'status', 'news-feed') add_image (bld, 'status', 'internet-news-reader')

View file

@ -1,6 +1,17 @@
/*
Copyright (C) 2011-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#include "katze/gtk3-compat.h" #include "katze/gtk3-compat.h"
#if !GTK_CHECK_VERSION (3, 2, 0) && !defined (HAVE_HILDON_2_2) #if !GTK_CHECK_VERSION (3, 2, 0)
static void static void
sokoke_widget_set_pango_font_style (GtkWidget* widget, sokoke_widget_set_pango_font_style (GtkWidget* widget,
PangoStyle style) PangoStyle style)
@ -27,9 +38,9 @@ sokoke_on_entry_focus_in_event (GtkEntry* entry,
g_object_get_data (G_OBJECT (entry), "sokoke_has_default")); g_object_get_data (G_OBJECT (entry), "sokoke_has_default"));
if (has_default) if (has_default)
{ {
gtk_entry_set_text (entry, "");
g_object_set_data (G_OBJECT (entry), "sokoke_has_default", g_object_set_data (G_OBJECT (entry), "sokoke_has_default",
GINT_TO_POINTER (0)); GINT_TO_POINTER (0));
gtk_entry_set_text (entry, "");
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry), sokoke_widget_set_pango_font_style (GTK_WIDGET (entry),
PANGO_STYLE_NORMAL); PANGO_STYLE_NORMAL);
} }
@ -46,9 +57,9 @@ sokoke_on_entry_focus_out_event (GtkEntry* entry,
{ {
const gchar* default_text = (const gchar*)g_object_get_data ( const gchar* default_text = (const gchar*)g_object_get_data (
G_OBJECT (entry), "sokoke_default_text"); G_OBJECT (entry), "sokoke_default_text");
gtk_entry_set_text (entry, default_text);
g_object_set_data (G_OBJECT (entry), g_object_set_data (G_OBJECT (entry),
"sokoke_has_default", GINT_TO_POINTER (1)); "sokoke_has_default", GINT_TO_POINTER (1));
gtk_entry_set_text (entry, default_text);
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry), sokoke_widget_set_pango_font_style (GTK_WIDGET (entry),
PANGO_STYLE_ITALIC); PANGO_STYLE_ITALIC);
} }
@ -71,14 +82,15 @@ gtk_entry_set_placeholder_text (GtkEntry* entry,
const gchar* default_text) const gchar* default_text)
{ {
/* Note: The default text initially overwrites any previous text */ /* Note: The default text initially overwrites any previous text */
gchar* old_value = g_object_get_data (G_OBJECT (entry), gchar* old_value = g_object_get_data (G_OBJECT (entry), "sokoke_default_text");
"sokoke_default_text"); g_object_set_data (G_OBJECT (entry), "sokoke_default_text", (gpointer)default_text);
if (!old_value)
if (default_text == NULL)
g_object_set_data (G_OBJECT (entry), "sokoke_has_default", GINT_TO_POINTER (0));
else if (!old_value)
{ {
g_object_set_data (G_OBJECT (entry), "sokoke_has_default", g_object_set_data (G_OBJECT (entry), "sokoke_has_default", GINT_TO_POINTER (1));
GINT_TO_POINTER (1)); sokoke_widget_set_pango_font_style (GTK_WIDGET (entry), PANGO_STYLE_ITALIC);
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry),
PANGO_STYLE_ITALIC);
gtk_entry_set_text (entry, default_text); gtk_entry_set_text (entry, default_text);
g_signal_connect (entry, "drag-data-received", g_signal_connect (entry, "drag-data-received",
G_CALLBACK (sokoke_on_entry_drag_data_received), NULL); G_CALLBACK (sokoke_on_entry_drag_data_received), NULL);
@ -89,55 +101,19 @@ gtk_entry_set_placeholder_text (GtkEntry* entry,
} }
else if (!gtk_widget_has_focus (GTK_WIDGET (entry))) else if (!gtk_widget_has_focus (GTK_WIDGET (entry)))
{ {
gint has_default = GPOINTER_TO_INT ( gint has_default = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (entry), "sokoke_has_default"));
g_object_get_data (G_OBJECT (entry), "sokoke_has_default"));
if (has_default) if (has_default)
{ {
gtk_entry_set_text (entry, default_text); gtk_entry_set_text (entry, default_text);
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry), sokoke_widget_set_pango_font_style (GTK_WIDGET (entry), PANGO_STYLE_ITALIC);
PANGO_STYLE_ITALIC);
} }
} }
g_object_set_data (G_OBJECT (entry), "sokoke_default_text", }
(gpointer)default_text);
const gchar*
gtk_entry_get_placeholder_text (GtkEntry* entry)
{
return g_object_get_data (G_OBJECT (entry), "sokoke_default_text");
} }
#endif #endif
#if !GTK_CHECK_VERSION (2, 12, 0)
void
gtk_widget_set_has_tooltip (GtkWidget* widget,
gboolean has_tooltip)
{
/* Do nothing */
}
void
gtk_widget_set_tooltip_text (GtkWidget* widget,
const gchar* text)
{
if (text && *text)
{
static GtkTooltips* tooltips = NULL;
if (G_UNLIKELY (!tooltips))
tooltips = gtk_tooltips_new ();
gtk_tooltips_set_tip (tooltips, widget, text, NULL);
}
}
void
gtk_tool_item_set_tooltip_text (GtkToolItem* toolitem,
const gchar* text)
{
if (text && *text)
{
static GtkTooltips* tooltips = NULL;
if (G_UNLIKELY (!tooltips))
tooltips = gtk_tooltips_new ();
gtk_tool_item_set_tooltip (toolitem, tooltips, text, NULL);
}
}
#endif

View file

@ -1,3 +1,14 @@
/*
Copyright (C) 2011-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
@ -6,7 +17,7 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#if GTK_CHECK_VERSION (3, 2, 0) #if GTK_CHECK_VERSION (3, 2, 0) && defined (GTK_DISABLE_DEPRECATED)
#define GTK_TYPE_VBOX GTK_TYPE_BOX #define GTK_TYPE_VBOX GTK_TYPE_BOX
#define GtkVBox GtkBox #define GtkVBox GtkBox
#define GtkVBoxClass GtkBoxClass #define GtkVBoxClass GtkBoxClass
@ -39,25 +50,6 @@ G_BEGIN_DECLS
#define g_format_size(sz) g_format_size_for_display ((goffset)sz) #define g_format_size(sz) g_format_size_for_display ((goffset)sz)
#endif #endif
#if !GTK_CHECK_VERSION (2, 14, 0)
#define gtk_dialog_get_content_area(dlg) dlg->vbox
#define gtk_dialog_get_action_area(dlg) dlg->action_area
#define gtk_widget_get_window(wdgt) wdgt->window
#define gtk_adjustment_get_page_size(adj) adj->page_size
#define gtk_adjustment_get_upper(adj) adj->upper
#define gtk_adjustment_get_lower(adj) adj->lower
#define gtk_adjustment_get_value(adj) adj->value
#endif
#if !GTK_CHECK_VERSION (2, 16, 0)
#define GTK_ACTIVATABLE GTK_WIDGET
#define gtk_activatable_get_related_action gtk_widget_get_action
#define gtk_menu_item_set_label(menuitem, label) \
gtk_label_set_label (GTK_LABEL (GTK_BIN (menuitem)->child), \
label ? label : "");
#define gtk_image_menu_item_set_always_show_image(menuitem, yesno) ()
#endif
#if !GTK_CHECK_VERSION (2, 18, 0) #if !GTK_CHECK_VERSION (2, 18, 0)
#define gtk_widget_is_toplevel(widget) GTK_WIDGET_TOPLEVEL (widget) #define gtk_widget_is_toplevel(widget) GTK_WIDGET_TOPLEVEL (widget)
#define gtk_widget_has_focus(widget) GTK_WIDGET_HAS_FOCUS (widget) #define gtk_widget_has_focus(widget) GTK_WIDGET_HAS_FOCUS (widget)
@ -87,26 +79,9 @@ G_BEGIN_DECLS
#define GTK_DIALOG_NO_SEPARATOR 0 #define GTK_DIALOG_NO_SEPARATOR 0
#endif #endif
#if !GTK_CHECK_VERSION (3, 2, 0) && defined (HAVE_HILDON_2_2) #if !GTK_CHECK_VERSION (3, 2, 0)
#define gtk_entry_set_placeholder_text hildon_gtk_entry_set_placeholder_text void gtk_entry_set_placeholder_text (GtkEntry* entry, const gchar* text);
#elif !GTK_CHECK_VERSION (3, 2, 0) const gchar* gtk_entry_get_placeholder_text (GtkEntry* entry);
#define gtk_entry_set_placeholder_text sokoke_entry_set_default_text
#endif
#if !GTK_CHECK_VERSION(2, 12, 0)
void
gtk_widget_set_has_tooltip (GtkWidget* widget,
gboolean has_tooltip);
void
gtk_widget_set_tooltip_text (GtkWidget* widget,
const gchar* text);
void
gtk_tool_item_set_tooltip_text (GtkToolItem* toolitem,
const gchar* text);
#endif #endif
#if !GTK_CHECK_VERSION (2, 24 ,0) #if !GTK_CHECK_VERSION (2, 24 ,0)
@ -149,6 +124,13 @@ gtk_tool_item_set_tooltip_text (GtkToolItem* toolitem,
#define GDK_KEY_Return GDK_Return #define GDK_KEY_Return GDK_Return
#endif #endif
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#ifndef GDK_IS_X11_DISPLAY
#define GDK_IS_X11_DISPLAY(display) TRUE
#endif
#endif
G_END_DECLS G_END_DECLS
#endif #endif

View file

@ -74,6 +74,13 @@ GList* kalistglobal;
static void static void
katze_array_finalize (GObject* object); katze_array_finalize (GObject* object);
static void
_katze_array_update (KatzeArray* array)
{
g_object_set_data (G_OBJECT (array), "last-update",
GINT_TO_POINTER (time (NULL)));
}
static void static void
_katze_array_add_item (KatzeArray* array, _katze_array_add_item (KatzeArray* array,
gpointer item) gpointer item)
@ -84,6 +91,7 @@ _katze_array_add_item (KatzeArray* array,
katze_item_set_parent (item, array); katze_item_set_parent (item, array);
array->items = g_list_append (array->items, item); array->items = g_list_append (array->items, item);
_katze_array_update (array);
} }
static void static void
@ -95,6 +103,7 @@ _katze_array_remove_item (KatzeArray* array,
if (KATZE_IS_ITEM (item)) if (KATZE_IS_ITEM (item))
katze_item_set_parent (item, NULL); katze_item_set_parent (item, NULL);
g_object_unref (item); g_object_unref (item);
_katze_array_update (array);
} }
static void static void
@ -104,6 +113,7 @@ _katze_array_move_item (KatzeArray* array,
{ {
array->items = g_list_remove (array->items, item); array->items = g_list_remove (array->items, item);
array->items = g_list_insert (array->items, item, position); array->items = g_list_insert (array->items, item, position);
_katze_array_update (array);
} }
static void static void
@ -112,9 +122,10 @@ _katze_array_clear (KatzeArray* array)
GObject* item; GObject* item;
while ((item = g_list_nth_data (array->items, 0))) while ((item = g_list_nth_data (array->items, 0)))
katze_array_remove_item (array, item); g_signal_emit (array, signals[REMOVE_ITEM], 0, item);
g_list_free (array->items); g_list_free (array->items);
array->items = NULL; array->items = NULL;
_katze_array_update (array);
} }
static void static void
@ -192,7 +203,7 @@ katze_array_class_init (KatzeArrayClass* class)
"update", "update",
G_TYPE_FROM_CLASS (class), G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0, G_STRUCT_OFFSET (KatzeArrayClass, update),
0, 0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__VOID,
@ -205,6 +216,7 @@ katze_array_class_init (KatzeArrayClass* class)
class->remove_item = _katze_array_remove_item; class->remove_item = _katze_array_remove_item;
class->move_item = _katze_array_move_item; class->move_item = _katze_array_move_item;
class->clear = _katze_array_clear; class->clear = _katze_array_clear;
class->update = _katze_array_update;
} }
static void static void
@ -217,14 +229,11 @@ katze_array_init (KatzeArray* array)
static void static void
katze_array_finalize (GObject* object) katze_array_finalize (GObject* object)
{ {
KatzeArray* array; KatzeArray* array = KATZE_ARRAY (object);
guint i; GList* items;
gpointer item;
array = KATZE_ARRAY (object); for (items = array->items; items; items = g_list_next (items))
i = 0; g_object_unref (items->data);
while ((item = g_list_nth_data (array->items, i++)))
g_object_unref (item);
g_list_free (array->items); g_list_free (array->items);
G_OBJECT_CLASS (katze_array_parent_class)->finalize (object); G_OBJECT_CLASS (katze_array_parent_class)->finalize (object);
@ -364,37 +373,44 @@ katze_array_get_item_index (KatzeArray* array,
/** /**
* katze_array_find_token: * katze_array_find_token:
* @array: a #KatzeArray * @array: a #KatzeArray
* @token: a token string * @token: a token string, or "token keywords" string
* *
* Looks up an item in the array which has the specified token. * Looks up an item in the array which has the specified token.
* *
* This function will silently fail if the type of the list * This function will fail if the type of the list
* is not based on #GObject and only #KatzeItem children * is not based on #KatzeItem children.
* are checked for their token, any other objects are skipped.
* *
* Note that @token is by definition unique to one item. * Note that @token is by definition unique to one item.
* *
* Since 0.4.4 @token can be a "token keywords" string.
*
* Return value: an item, or %NULL * Return value: an item, or %NULL
**/ **/
gpointer gpointer
katze_array_find_token (KatzeArray* array, katze_array_find_token (KatzeArray* array,
const gchar* token) const gchar* token)
{ {
guint i; goffset token_length;
gpointer item; GList* items;
g_return_val_if_fail (KATZE_IS_ARRAY (array), NULL); g_return_val_if_fail (KATZE_IS_ARRAY (array), NULL);
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), NULL);
g_return_val_if_fail (token != NULL, NULL);
i = 0; token_length = strchr (token, ' ') - token;
while ((item = g_list_nth_data (array->items, i++))) if (token_length < 1)
token_length = strlen (token);
for (items = array->items; items; items = g_list_next (items))
{ {
const gchar* found_token; const gchar* found_token = ((KatzeItem*)items->data)->token;
if (found_token != NULL)
{
guint bigger_item = strlen (found_token) > token_length ? strlen (found_token) : token_length;
if (!KATZE_IS_ITEM (item)) if (strncmp (token, found_token, bigger_item) == 0)
continue; return items->data;
found_token = ((KatzeItem*)item)->token; }
if (!g_strcmp0 (found_token, token))
return item;
} }
return NULL; return NULL;
} }
@ -406,9 +422,8 @@ katze_array_find_token (KatzeArray* array,
* *
* Looks up an item in the array which has the specified URI. * Looks up an item in the array which has the specified URI.
* *
* This function will silently fail if the type of the list * This function will fail if the type of the list
* is not based on #GObject and only #KatzeItem children * is not based on #KatzeItem children.
* are checked for their token, any other objects are skipped.
* *
* Return value: an item, or %NULL * Return value: an item, or %NULL
* *
@ -418,19 +433,17 @@ gpointer
katze_array_find_uri (KatzeArray* array, katze_array_find_uri (KatzeArray* array,
const gchar* uri) const gchar* uri)
{ {
guint i; GList* items;
gpointer item;
i = 0; g_return_val_if_fail (KATZE_IS_ARRAY (array), NULL);
while ((item = g_list_nth_data (array->items, i++))) g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), NULL);
g_return_val_if_fail (uri != NULL, NULL);
for (items = array->items; items; items = g_list_next (items))
{ {
const gchar* found_uri; const gchar* found_uri = ((KatzeItem*)items->data)->uri;
if (found_uri != NULL && !strcmp (found_uri, uri))
if (!KATZE_IS_ITEM (item)) return items->data;
continue;
found_uri = ((KatzeItem*)item)->uri;
if (!g_strcmp0 (found_uri, uri))
return item;
} }
return NULL; return NULL;
} }

View file

@ -355,11 +355,16 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
gint summand; gint summand;
KatzeItem* item; KatzeItem* item;
GtkWidget* menuitem; GtkWidget* menuitem;
const gchar* icon_name;
GdkPixbuf* icon;
GtkWidget* image; GtkWidget* image;
GtkWidget* submenu; GtkWidget* submenu;
g_return_if_fail (KATZE_IS_ARRAY_ACTION (array_action));
g_return_if_fail (KATZE_IS_ITEM (array));
g_return_if_fail (GTK_IS_MENU_SHELL (menu));
g_return_if_fail (GTK_IS_TOOL_ITEM (proxy)
|| GTK_IS_MENU_ITEM (proxy)
|| GTK_IS_WINDOW (proxy));
if (!KATZE_IS_ARRAY (array)) if (!KATZE_IS_ARRAY (array))
return; return;
@ -385,18 +390,7 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
} }
menuitem = katze_image_menu_item_new_ellipsized ( menuitem = katze_image_menu_item_new_ellipsized (
katze_item_get_name (item)); katze_item_get_name (item));
if ((icon_name = katze_item_get_icon (item)) && *icon_name) image = katze_item_get_image (item, menuitem);
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
{
if (KATZE_ITEM_IS_FOLDER (item))
icon = gtk_widget_render_icon (menuitem,
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
else
icon = katze_load_cached_icon (katze_item_get_uri (item), proxy);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
}
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
gtk_image_menu_item_set_always_show_image ( gtk_image_menu_item_set_always_show_image (
GTK_IMAGE_MENU_ITEM (menuitem), TRUE); GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
@ -406,8 +400,13 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
{ {
submenu = gtk_menu_new (); submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
/* Make sure menu appears to contain items */
gtk_menu_shell_append (GTK_MENU_SHELL (submenu),
gtk_separator_menu_item_new ());
g_signal_connect (menuitem, "select", g_signal_connect (menuitem, "select",
G_CALLBACK (katze_array_action_menu_item_select_cb), array_action); G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
g_signal_connect (menuitem, "activate",
G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
} }
else else
{ {
@ -421,21 +420,39 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
} }
} }
static void static gboolean
katze_array_action_menu_item_select_cb (GtkWidget* proxy, katze_array_action_menu_item_need_update (KatzeArrayAction* array_action,
KatzeArrayAction* array_action) GtkWidget* proxy)
{ {
GtkWidget* menu; GtkWidget* menu;
KatzeArray* array; KatzeArray* array;
gint last_array_update, last_proxy_update;
gboolean handled; gboolean handled;
array = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
/* last-update is set on all arrays; consider public API */
last_array_update = GPOINTER_TO_INT (
g_object_get_data (G_OBJECT (array), "last-update"));
last_proxy_update = GPOINTER_TO_INT (
g_object_get_data (G_OBJECT (proxy), "last-update"));
if (last_proxy_update > last_array_update)
return FALSE;
menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)); menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
gtk_container_foreach (GTK_CONTAINER (menu), gtk_container_foreach (GTK_CONTAINER (menu),
(GtkCallback)(gtk_widget_destroy), NULL); (GtkCallback)(gtk_widget_destroy), NULL);
array = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
katze_array_action_generate_menu (array_action, array, GTK_MENU_SHELL (menu), proxy); katze_array_action_generate_menu (array_action, array, GTK_MENU_SHELL (menu), proxy);
g_signal_emit (array_action, signals[POPULATE_FOLDER], 0, menu, array, &handled); g_signal_emit (array_action, signals[POPULATE_FOLDER], 0, menu, array, &handled);
g_object_set_data (G_OBJECT (proxy), "last-update",
GINT_TO_POINTER (time (NULL)));
return TRUE;
}
static void
katze_array_action_menu_item_select_cb (GtkWidget* proxy,
KatzeArrayAction* array_action)
{
katze_array_action_menu_item_need_update (array_action, proxy);
} }
static void static void
@ -458,20 +475,21 @@ katze_array_action_proxy_clicked_cb (GtkWidget* proxy,
KatzeArray* array; KatzeArray* array;
gboolean handled = FALSE; gboolean handled = FALSE;
array = (KatzeArray*)g_object_get_data (G_OBJECT (proxy), "KatzeItem");
if (GTK_IS_MENU_ITEM (proxy)) if (GTK_IS_MENU_ITEM (proxy))
{ {
g_object_set_data (G_OBJECT (proxy), "KatzeItem", array_action->array); if (katze_array_action_menu_item_need_update (array_action, proxy))
katze_array_action_menu_item_select_cb (proxy, array_action); {
g_signal_emit (array_action, signals[POPULATE_FOLDER], 0, g_signal_emit (array_action, signals[POPULATE_FOLDER], 0,
gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)), gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)),
array_action->array, &handled); array, &handled);
if (!handled) if (!handled)
g_signal_emit (array_action, signals[POPULATE_POPUP], 0, g_signal_emit (array_action, signals[POPULATE_POPUP], 0,
gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy))); gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)));
}
return; return;
} }
array = (KatzeArray*)g_object_get_data (G_OBJECT (proxy), "KatzeArray");
if (KATZE_IS_ITEM (array) && katze_item_get_uri ((KatzeItem*)array)) if (KATZE_IS_ITEM (array) && katze_item_get_uri ((KatzeItem*)array))
{ {
katze_array_action_activate_item (array_action, KATZE_ITEM (array), 1); katze_array_action_activate_item (array_action, KATZE_ITEM (array), 1);
@ -493,14 +511,8 @@ katze_array_action_proxy_clicked_cb (GtkWidget* proxy,
g_signal_emit (array_action, signals[POPULATE_POPUP], 0, menu); g_signal_emit (array_action, signals[POPULATE_POPUP], 0, menu);
} }
#if HAVE_HILDON
/* Avoid a bug in GTK+ messing up the initial scrolling position */
katze_widget_popup (NULL, GTK_MENU (menu),
NULL, KATZE_MENU_POSITION_LEFT);
#else
katze_widget_popup (GTK_WIDGET (proxy), GTK_MENU (menu), katze_widget_popup (GTK_WIDGET (proxy), GTK_MENU (menu),
NULL, KATZE_MENU_POSITION_LEFT); NULL, KATZE_MENU_POSITION_LEFT);
#endif
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), TRUE); gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), TRUE);
g_object_set_data (G_OBJECT (menu), "KatzeArrayAction", array_action); g_object_set_data (G_OBJECT (menu), "KatzeArrayAction", array_action);
g_signal_connect (menu, "deactivate", g_signal_connect (menu, "deactivate",
@ -552,7 +564,6 @@ katze_array_action_item_notify_cb (KatzeItem* item,
const gchar* property; const gchar* property;
const gchar* title; const gchar* title;
const gchar* desc; const gchar* desc;
GdkPixbuf* icon;
GtkWidget* image; GtkWidget* image;
if (!G_IS_PARAM_SPEC_STRING (pspec)) if (!G_IS_PARAM_SPEC_STRING (pspec))
@ -579,17 +590,12 @@ katze_array_action_item_notify_cb (KatzeItem* item,
} }
else if (KATZE_ITEM_IS_BOOKMARK (item) && !strcmp (property, "uri")) else if (KATZE_ITEM_IS_BOOKMARK (item) && !strcmp (property, "uri"))
{ {
icon = katze_load_cached_icon (katze_item_get_uri (item), GTK_WIDGET (toolitem)); image = katze_item_get_image (item, GTK_WIDGET (toolitem));
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
} }
else if (!strcmp (property, "icon")) else if (!strcmp (property, "icon"))
{ {
image = gtk_image_new_from_icon_name (katze_item_get_icon (item), image = katze_item_get_image (item, GTK_WIDGET (toolitem));
GTK_ICON_SIZE_MENU);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
} }
} }
@ -600,25 +606,12 @@ katze_array_action_proxy_create_menu_proxy_cb (GtkWidget* proxy,
{ {
KatzeArrayAction* array_action; KatzeArrayAction* array_action;
GtkWidget* menuitem; GtkWidget* menuitem;
const gchar* icon_name;
GtkWidget* image; GtkWidget* image;
GdkPixbuf* icon;
array_action = g_object_get_data (G_OBJECT (proxy), "KatzeArrayAction"); array_action = g_object_get_data (G_OBJECT (proxy), "KatzeArrayAction");
menuitem = katze_image_menu_item_new_ellipsized ( menuitem = katze_image_menu_item_new_ellipsized (
katze_item_get_name (item)); katze_item_get_name (item));
if ((icon_name = katze_item_get_icon (item)) && *icon_name) image = katze_item_get_image (item, menuitem);
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
{
if (KATZE_ITEM_IS_FOLDER (item))
icon = gtk_widget_render_icon (menuitem,
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
else
icon = katze_load_cached_icon (katze_item_get_uri (item), proxy);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
}
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
gtk_image_menu_item_set_always_show_image ( gtk_image_menu_item_set_always_show_image (
GTK_IMAGE_MENU_ITEM (menuitem), TRUE); GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
@ -674,7 +667,6 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
const gchar* uri; const gchar* uri;
const gchar* desc; const gchar* desc;
GtkToolItem* toolitem; GtkToolItem* toolitem;
GdkPixbuf* icon;
GtkWidget* image; GtkWidget* image;
GtkWidget* label; GtkWidget* label;
@ -686,21 +678,12 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
return gtk_separator_tool_item_new (); return gtk_separator_tool_item_new ();
if (KATZE_ITEM_IS_FOLDER (item)) if (KATZE_ITEM_IS_FOLDER (item))
{
toolitem = gtk_toggle_tool_button_new (); toolitem = gtk_toggle_tool_button_new ();
icon = gtk_widget_render_icon (GTK_WIDGET (toolitem),
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
}
else else
{
toolitem = gtk_tool_button_new (NULL, ""); toolitem = gtk_tool_button_new (NULL, "");
icon = katze_load_cached_icon (uri, GTK_WIDGET (toolitem));
}
g_signal_connect (toolitem, "create-menu-proxy", g_signal_connect (toolitem, "create-menu-proxy",
G_CALLBACK (katze_array_action_proxy_create_menu_proxy_cb), item); G_CALLBACK (katze_array_action_proxy_create_menu_proxy_cb), item);
image = gtk_image_new_from_pixbuf (icon); image = katze_item_get_image (item, GTK_WIDGET (toolitem));
g_object_unref (icon);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
label = gtk_label_new (NULL); label = gtk_label_new (NULL);
/* FIXME: Should text direction be respected here? */ /* FIXME: Should text direction be respected here? */
@ -739,6 +722,9 @@ static void
katze_array_action_connect_proxy (GtkAction* action, katze_array_action_connect_proxy (GtkAction* action,
GtkWidget* proxy) GtkWidget* proxy)
{ {
KatzeArrayAction* array_action = KATZE_ARRAY_ACTION (action);
g_object_set_data (G_OBJECT (proxy), "KatzeItem", array_action->array);
GTK_ACTION_CLASS (katze_array_action_parent_class)->connect_proxy ( GTK_ACTION_CLASS (katze_array_action_parent_class)->connect_proxy (
action, proxy); action, proxy);
@ -750,9 +736,10 @@ katze_array_action_connect_proxy (GtkAction* action,
else if (GTK_IS_MENU_ITEM (proxy)) else if (GTK_IS_MENU_ITEM (proxy))
{ {
gtk_menu_item_set_submenu (GTK_MENU_ITEM (proxy), gtk_menu_new ()); gtk_menu_item_set_submenu (GTK_MENU_ITEM (proxy), gtk_menu_new ());
/* FIXME: 'select' doesn't cover all ways of selection */
g_signal_connect (proxy, "select", g_signal_connect (proxy, "select",
G_CALLBACK (katze_array_action_proxy_clicked_cb), action); G_CALLBACK (katze_array_action_proxy_clicked_cb), action);
g_signal_connect (proxy, "activate",
G_CALLBACK (katze_array_action_proxy_clicked_cb), action);
} }
} }

View file

@ -27,7 +27,7 @@
#include <sqlite3.h> #include <sqlite3.h>
#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;" #define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)" #define CREATE_TABLE "CREATE TABLE IF NOT EXISTS moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);" #define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;" #define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
@ -71,80 +71,6 @@ G_DEFINE_TYPE_WITH_CODE (KatzeHttpCookiesSqlite, katze_http_cookies_sqlite, G_TY
Copyright (C) 2009 Collabora Ltd. Copyright (C) 2009 Collabora Ltd.
Mostly copied from libSoup 2.30, coding style retained */ Mostly copied from libSoup 2.30, coding style retained */
static void
try_create_table (sqlite3 *db)
{
char *error = NULL;
if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
}
static void
exec_query_with_try_create_table (sqlite3* db,
const char* sql,
int (*callback)(void*,int,char**,char**),
void *argument)
{
char *error = NULL;
gboolean try_create = TRUE;
try_exec:
if (sqlite3_exec (db, sql, callback, argument, &error)) {
if (try_create) {
try_create = FALSE;
try_create_table (db);
sqlite3_free (error);
error = NULL;
goto try_exec;
} else {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
}
}
static int
callback (void *data, int argc, char **argv, char **colname)
{
SoupCookie *cookie = NULL;
SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
char *name, *value, *host, *path;
gint64 expire_time;
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
now = time (NULL);
name = argv[COL_NAME];
value = argv[COL_VALUE];
host = argv[COL_HOST];
path = argv[COL_PATH];
expire_time = g_ascii_strtoull (argv[COL_EXPIRY], NULL, 10);
if (now >= expire_time)
return 0;
max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
cookie = soup_cookie_new (name, value, host, path, max_age);
if (secure)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
soup_cookie_jar_add_cookie (jar, cookie);
return 0;
}
/* Follows sqlite3 convention; returns TRUE on error */ /* Follows sqlite3 convention; returns TRUE on error */
static gboolean static gboolean
katze_http_cookies_sqlite_open_db (KatzeHttpCookiesSqlite* http_cookies) katze_http_cookies_sqlite_open_db (KatzeHttpCookiesSqlite* http_cookies)
@ -157,23 +83,91 @@ katze_http_cookies_sqlite_open_db (KatzeHttpCookiesSqlite* http_cookies)
return TRUE; return TRUE;
} }
if (sqlite3_exec (http_cookies->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) { if (sqlite3_exec (http_cookies->db, CREATE_TABLE, NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error); g_warning ("Failed to execute query: %s", error);
sqlite3_free (error); sqlite3_free (error);
} }
if (sqlite3_exec (http_cookies->db, "PRAGMA secure_delete = 1;",
NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
sqlite3_exec (http_cookies->db,
/* Arguably cookies are like a cache, so performance over integrity */
"PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY;"
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
NULL, NULL, &error);
return FALSE; return FALSE;
} }
static void static void
katze_http_cookies_sqlite_load (KatzeHttpCookiesSqlite* http_cookies) katze_http_cookies_sqlite_load (KatzeHttpCookiesSqlite* http_cookies)
{ {
const char *name, *value, *host, *path;
sqlite3_stmt* stmt;
SoupCookie *cookie = NULL;
gint64 expire_time;
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
char *query;
int result;
if (http_cookies->db == NULL) { if (http_cookies->db == NULL) {
if (katze_http_cookies_sqlite_open_db (http_cookies)) if (katze_http_cookies_sqlite_open_db (http_cookies))
return; return;
} }
exec_query_with_try_create_table (http_cookies->db, QUERY_ALL, callback, http_cookies->jar); sqlite3_prepare_v2 (http_cookies->db, QUERY_ALL, strlen (QUERY_ALL) + 1, &stmt, NULL);
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to load cookies\n"));
sqlite3_reset (stmt);
return;
}
while (result == SQLITE_ROW)
{
now = time (NULL);
name = (const char*)sqlite3_column_text (stmt, COL_NAME);
value = (const char*)sqlite3_column_text (stmt, COL_VALUE);
host = (const char*)sqlite3_column_text (stmt, COL_HOST);
path = (const char*)sqlite3_column_text (stmt, COL_PATH);
expire_time = sqlite3_column_int64 (stmt,COL_EXPIRY);
secure = sqlite3_column_int (stmt, COL_SECURE);
http_only = sqlite3_column_int (stmt, COL_HTTP_ONLY);
if (now >= expire_time)
{
/* Cookie expired, remove it from database */
query = sqlite3_mprintf (QUERY_DELETE, name, host);
sqlite3_exec (http_cookies->db, QUERY_DELETE, NULL, NULL, NULL);
sqlite3_free (query);
result = sqlite3_step (stmt);
continue;
}
max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
cookie = soup_cookie_new (name, value, host, path, max_age);
if (secure)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
soup_cookie_jar_add_cookie (http_cookies->jar, cookie);
result = sqlite3_step (stmt);
}
if (stmt)
{
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
}
} }
static void static void
katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar, katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
@ -213,14 +207,14 @@ katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
} }
} }
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL) if (!g_strcmp0 (g_getenv ("MIDORI_DEBUG"), "cookies"))
http_cookies->counter++; http_cookies->counter++;
if (old_cookie) { if (old_cookie) {
query = sqlite3_mprintf (QUERY_DELETE, query = sqlite3_mprintf (QUERY_DELETE,
old_cookie->name, old_cookie->name,
old_cookie->domain); old_cookie->domain);
exec_query_with_try_create_table (http_cookies->db, query, NULL, NULL); sqlite3_exec (http_cookies->db, query, NULL, NULL, NULL);
sqlite3_free (query); sqlite3_free (query);
} }
@ -234,7 +228,7 @@ katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
expires, expires,
new_cookie->secure, new_cookie->secure,
new_cookie->http_only); new_cookie->http_only);
exec_query_with_try_create_table (http_cookies->db, query, NULL, NULL); sqlite3_exec (http_cookies->db, query, NULL, NULL, NULL);
sqlite3_free (query); sqlite3_free (query);
} }
} }

View file

@ -14,6 +14,7 @@
#endif #endif
#include "katze-http-cookies.h" #include "katze-http-cookies.h"
#include "midori/midori-core.h"
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
@ -211,7 +212,7 @@ katze_http_cookies_update_jar (KatzeHttpCookies* http_cookies)
goto failed; goto failed;
g_free (temporary_filename); g_free (temporary_filename);
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL) if (!g_strcmp0 (g_getenv ("MIDORI_DEBUG"), "cookies"))
{ {
g_print ("KatzeHttpCookies: %d cookies changed\n", http_cookies->counter); g_print ("KatzeHttpCookies: %d cookies changed\n", http_cookies->counter);
http_cookies->counter = 0; http_cookies->counter = 0;
@ -223,7 +224,7 @@ failed:
fclose (f); fclose (f);
g_unlink (temporary_filename); g_unlink (temporary_filename);
g_free (temporary_filename); g_free (temporary_filename);
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL) if (!g_strcmp0 (g_getenv ("MIDORI_DEBUG"), "cookies"))
g_print ("KatzeHttpCookies: Failed to write '%s'\n", g_print ("KatzeHttpCookies: Failed to write '%s'\n",
http_cookies->filename); http_cookies->filename);
return FALSE; return FALSE;
@ -263,12 +264,12 @@ katze_http_cookies_jar_changed_cb (SoupCookieJar* jar,
} }
} }
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL) if (!g_strcmp0 (g_getenv ("MIDORI_DEBUG"), "cookies"))
http_cookies->counter++; http_cookies->counter++;
if (!http_cookies->timeout && (old_cookie || new_cookie->expires)) if (!http_cookies->timeout && (old_cookie || (new_cookie && new_cookie->expires)))
http_cookies->timeout = g_timeout_add_seconds (5, http_cookies->timeout = midori_timeout_add_seconds (
(GSourceFunc)katze_http_cookies_update_jar, http_cookies); 5, (GSourceFunc)katze_http_cookies_update_jar, http_cookies, NULL);
} }
static void static void

View file

@ -10,11 +10,13 @@
*/ */
#include "katze-item.h" #include "katze-item.h"
#include "katze-utils.h" #include "katze-utils.h"
#include "midori/midori-core.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include "katze/katze.h"
/** /**
* SECTION:katze-item * SECTION:katze-item
* @short_description: A useful item * @short_description: A useful item
@ -315,6 +317,8 @@ katze_item_set_name (KatzeItem* item,
g_return_if_fail (KATZE_IS_ITEM (item)); g_return_if_fail (KATZE_IS_ITEM (item));
katze_assign (item->name, g_strdup (name)); katze_assign (item->name, g_strdup (name));
if (item->parent)
katze_array_update ((KatzeArray*)item->parent);
g_object_notify (G_OBJECT (item), "name"); g_object_notify (G_OBJECT (item), "name");
} }
@ -380,6 +384,9 @@ katze_item_set_uri (KatzeItem* item,
{ {
g_return_if_fail (KATZE_IS_ITEM (item)); g_return_if_fail (KATZE_IS_ITEM (item));
if (!g_strcmp0 (item->uri, uri))
return;
katze_assign (item->uri, g_strdup (uri)); katze_assign (item->uri, g_strdup (uri));
g_object_notify (G_OBJECT (item), "uri"); g_object_notify (G_OBJECT (item), "uri");
} }
@ -414,9 +421,128 @@ katze_item_set_icon (KatzeItem* item,
g_return_if_fail (KATZE_IS_ITEM (item)); g_return_if_fail (KATZE_IS_ITEM (item));
katze_item_set_meta_string (item, "icon", icon); katze_item_set_meta_string (item, "icon", icon);
if (item->parent)
katze_array_update ((KatzeArray*)item->parent);
g_object_notify (G_OBJECT (item), "icon"); g_object_notify (G_OBJECT (item), "icon");
} }
/**
* katze_item_get_pixbuf:
* @item: a #KatzeItem
* @widget: a #GtkWidget, or %NULL
*
* Retrieves a #GdkPixbuf fit to display @item.
*
* Return value: the icon of the item
*
* Since: 0.4.6
**/
GdkPixbuf*
katze_item_get_pixbuf (KatzeItem* item,
GtkWidget* widget)
{
GdkPixbuf* pixbuf;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
if (widget && KATZE_ITEM_IS_FOLDER (item))
return gtk_widget_render_icon (widget, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
if ((pixbuf = midori_paths_get_icon (katze_item_get_icon (item), NULL)))
return pixbuf;
if ((pixbuf = midori_paths_get_icon (item->uri, widget)))
return pixbuf;
return NULL;
}
static void
katze_item_image_destroyed_cb (GtkWidget* image,
KatzeItem* item);
#ifndef HAVE_WEBKIT2
#if WEBKIT_CHECK_VERSION (1, 3, 13)
static void
#if WEBKIT_CHECK_VERSION (1, 8, 0)
katze_item_icon_loaded_cb (WebKitFaviconDatabase* database,
#elif WEBKIT_CHECK_VERSION (1, 3, 13)
katze_item_icon_loaded_cb (WebKitIconDatabase* database,
WebKitWebFrame* web_frame,
#endif
const gchar* frame_uri,
GtkWidget* image)
{
KatzeItem* item = g_object_get_data (G_OBJECT (image), "KatzeItem");
GdkPixbuf* pixbuf;
if (!strcmp (frame_uri, item->uri)
&& (pixbuf = midori_paths_get_icon (frame_uri, image)))
{
gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
g_object_unref (pixbuf);
/* This signal fires extremely often (WebKit bug?)
we must throttle it (disconnect) once we have an icon */
katze_item_image_destroyed_cb (image, g_object_ref (item));
}
}
#endif
#endif
static void
katze_item_image_destroyed_cb (GtkWidget* image,
KatzeItem* item)
{
#ifndef HAVE_WEBKIT2
#if WEBKIT_CHECK_VERSION (1, 8, 0)
g_signal_handlers_disconnect_by_func (webkit_get_favicon_database (),
katze_item_icon_loaded_cb, image);
#elif WEBKIT_CHECK_VERSION (1, 3, 13)
g_signal_handlers_disconnect_by_func (webkit_get_icon_database (),
katze_item_icon_loaded_cb, image);
#endif
#endif
g_object_unref (item);
}
/**
* katze_item_get_image:
* @item: a #KatzeItem
* @widget: a #GtkWidget, or %NULL
*
* Retrieves a #GtkImage fit to display @item.
*
* Return value: the icon of the item
*
* Since: 0.4.4
* Since 0.4.8 a @widget was added and the image is visible.
**/
GtkWidget*
katze_item_get_image (KatzeItem* item,
GtkWidget* widget)
{
GtkWidget* image;
GdkPixbuf* pixbuf;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
pixbuf = katze_item_get_pixbuf (item, widget);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_widget_show (image);
if (pixbuf != NULL)
g_object_unref (pixbuf);
if (KATZE_ITEM_IS_FOLDER (item))
return image;
g_object_set_data (G_OBJECT (image), "KatzeItem", g_object_ref (item));
g_signal_connect (image, "destroy",
G_CALLBACK (katze_item_image_destroyed_cb), item);
#ifndef HAVE_WEBKIT2
#if WEBKIT_CHECK_VERSION (1, 8, 0)
g_signal_connect (webkit_get_favicon_database (), "icon-loaded",
G_CALLBACK (katze_item_icon_loaded_cb), image);
#elif WEBKIT_CHECK_VERSION (1, 3, 13)
g_signal_connect (webkit_get_icon_database (), "icon-loaded",
G_CALLBACK (katze_item_icon_loaded_cb), image);
#endif
#endif
return image;
}
/** /**
* katze_item_get_token: * katze_item_get_token:
* @item: a #KatzeItem * @item: a #KatzeItem
@ -527,17 +653,22 @@ katze_item_set_meta_data_value (KatzeItem* item,
* Return value: a string, or %NULL * Return value: a string, or %NULL
* *
* Since: 0.1.8 * Since: 0.1.8
*
* Since 0.4.4 "" is treated like %NULL.
**/ **/
const gchar* const gchar*
katze_item_get_meta_string (KatzeItem* item, katze_item_get_meta_string (KatzeItem* item,
const gchar* key) const gchar* key)
{ {
const gchar* value;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL); g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
g_return_val_if_fail (key != NULL, NULL); g_return_val_if_fail (key != NULL, NULL);
if (g_str_has_prefix (key, "midori:")) if (g_str_has_prefix (key, "midori:"))
key = &key[7]; key = &key[7];
return g_hash_table_lookup (item->metadata, key); value = g_hash_table_lookup (item->metadata, key);
return value && *value ? value : NULL;
} }
/** /**

View file

@ -12,7 +12,7 @@
#ifndef __KATZE_ITEM_H__ #ifndef __KATZE_ITEM_H__
#define __KATZE_ITEM_H__ #define __KATZE_ITEM_H__
#include <glib-object.h> #include <gtk/gtk.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -91,6 +91,14 @@ void
katze_item_set_icon (KatzeItem* item, katze_item_set_icon (KatzeItem* item,
const gchar* icon); const gchar* icon);
GdkPixbuf*
katze_item_get_pixbuf (KatzeItem* item,
GtkWidget* widget);
GtkWidget*
katze_item_get_image (KatzeItem* item,
GtkWidget* widget);
const gchar* const gchar*
katze_item_get_token (KatzeItem* item); katze_item_get_token (KatzeItem* item);

View file

@ -22,14 +22,10 @@
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <libsoup/soup.h> #include <libsoup/soup.h>
#include <webkit/webkit.h>
struct _KatzeNet struct _KatzeNet
{ {
GObject parent_instance; GObject parent_instance;
gchar* cache_path;
guint cache_size;
}; };
struct _KatzeNetClass struct _KatzeNetClass
@ -54,37 +50,14 @@ katze_net_class_init (KatzeNetClass* class)
static void static void
katze_net_init (KatzeNet* net) katze_net_init (KatzeNet* net)
{ {
net->cache_path = g_build_filename (g_get_user_cache_dir (),
PACKAGE_NAME, NULL);
} }
static void static void
katze_net_finalize (GObject* object) katze_net_finalize (GObject* object)
{ {
KatzeNet* net = KATZE_NET (object);
katze_assign (net->cache_path, NULL);
G_OBJECT_CLASS (katze_net_parent_class)->finalize (object); G_OBJECT_CLASS (katze_net_parent_class)->finalize (object);
} }
static KatzeNet*
katze_net_new (void)
{
static KatzeNet* net = NULL;
if (!net)
{
net = g_object_new (KATZE_TYPE_NET, NULL);
/* Since this is a "singleton", keep an extra reference */
g_object_ref (net);
}
else
g_object_ref (net);
return net;
}
typedef struct typedef struct
{ {
KatzeNetStatusCb status_cb; KatzeNetStatusCb status_cb;
@ -104,36 +77,40 @@ katze_net_priv_free (KatzeNetPriv* priv)
g_slice_free (KatzeNetPriv, priv); g_slice_free (KatzeNetPriv, priv);
} }
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
gchar* gchar*
katze_net_get_cached_path (KatzeNet* net, katze_net_get_cached_path (KatzeNet* net,
const gchar* uri, const gchar* uri,
const gchar* subfolder) const gchar* subfolder)
{ {
gchar* cache_path;
gchar* checksum; gchar* checksum;
gchar* extension; gchar* extension;
gchar* cached_filename; gchar* cached_filename;
gchar* cached_path; gchar* cached_path;
net = katze_net_new (); if (uri == NULL)
return NULL;
if (subfolder)
cache_path = g_build_filename (net->cache_path, subfolder, NULL);
else
cache_path = net->cache_path;
katze_mkdir_with_parents (cache_path, 0700);
checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
extension = g_strrstr (uri, "."); extension = g_strrstr (uri, ".");
cached_filename = g_strdup_printf ("%s%s", checksum, cached_filename = g_strdup_printf ("%s%s", checksum,
extension ? extension : ""); extension ? extension : "");
g_free (checksum); g_free (checksum);
cached_path = g_build_filename (cache_path, cached_filename, NULL);
g_free (cached_filename);
if (subfolder) if (subfolder)
{
gchar* cache_path = g_build_filename (midori_paths_get_cache_dir_for_reading (), subfolder, NULL);
katze_mkdir_with_parents (cache_path, 0700);
cached_path = g_build_filename (cache_path, cached_filename, NULL);
g_free (cache_path); g_free (cache_path);
}
else
cached_path = g_build_filename (midori_paths_get_cache_dir (), cached_filename, NULL);
g_free (cached_filename);
return cached_path; return cached_path;
} }
#endif
static void static void
katze_net_got_body_cb (SoupMessage* msg, katze_net_got_body_cb (SoupMessage* msg,
@ -143,6 +120,7 @@ static void
katze_net_got_headers_cb (SoupMessage* msg, katze_net_got_headers_cb (SoupMessage* msg,
KatzeNetPriv* priv) KatzeNetPriv* priv)
{ {
#ifndef HAVE_WEBKIT2
KatzeNetRequest* request = priv->request; KatzeNetRequest* request = priv->request;
switch (msg->status_code) switch (msg->status_code)
@ -163,6 +141,7 @@ katze_net_got_headers_cb (SoupMessage* msg,
g_signal_handlers_disconnect_by_func (msg, katze_net_got_body_cb, priv); g_signal_handlers_disconnect_by_func (msg, katze_net_got_body_cb, priv);
soup_session_cancel_message (webkit_get_default_session (), msg, 1); soup_session_cancel_message (webkit_get_default_session (), msg, 1);
} }
#endif
} }
static void static void
@ -264,6 +243,7 @@ katze_net_load_uri (KatzeNet* net,
KatzeNetTransferCb transfer_cb, KatzeNetTransferCb transfer_cb,
gpointer user_data) gpointer user_data)
{ {
#ifndef HAVE_WEBKIT2
KatzeNetRequest* request; KatzeNetRequest* request;
KatzeNetPriv* priv; KatzeNetPriv* priv;
SoupMessage* msg; SoupMessage* msg;
@ -303,5 +283,6 @@ katze_net_load_uri (KatzeNet* net,
g_idle_add ((GSourceFunc)katze_net_local_cb, priv); g_idle_add ((GSourceFunc)katze_net_local_cb, priv);
else else
g_idle_add ((GSourceFunc)katze_net_default_cb, priv); g_idle_add ((GSourceFunc)katze_net_default_cb, priv);
#endif
} }

View file

@ -12,10 +12,13 @@
#ifndef __KATZE_NET_H__ #ifndef __KATZE_NET_H__
#define __KATZE_NET_H__ #define __KATZE_NET_H__
#ifndef HAVE_WEBKIT2
#include <webkit/webkit.h>
#else
#include <webkit2/webkit2.h>
#endif
#include "katze-utils.h" #include "katze-utils.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define KATZE_TYPE_NET \ #define KATZE_TYPE_NET \
@ -68,10 +71,12 @@ katze_net_load_uri (KatzeNet* net,
KatzeNetTransferCb transfer_cb, KatzeNetTransferCb transfer_cb,
gpointer user_data); gpointer user_data);
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
gchar* gchar*
katze_net_get_cached_path (KatzeNet* net, katze_net_get_cached_path (KatzeNet* net,
const gchar* uri, const gchar* uri,
const gchar* subfolder); const gchar* subfolder);
#endif
G_END_DECLS G_END_DECLS

View file

@ -15,9 +15,11 @@
#include <config.h> #include <config.h>
#endif #endif
#if HAVE_HILDON #ifdef HAVE_GRANITE
#include "katze-scrolled.h" #if HAVE_OSX
#include <hildon/hildon.h> #error FIXME granite on OSX is not implemented
#endif
#include <granite.h>
#endif #endif
#include <string.h> #include <string.h>
@ -25,13 +27,6 @@
struct _KatzePreferencesPrivate struct _KatzePreferencesPrivate
{ {
#if HAVE_HILDON
GtkWidget* scrolled;
GtkSizeGroup* sizegroup;
GtkSizeGroup* sizegroup2;
GtkWidget* box;
GtkWidget* hbox;
#else
GtkWidget* notebook; GtkWidget* notebook;
GtkWidget* toolbar; GtkWidget* toolbar;
GtkWidget* toolbutton; GtkWidget* toolbutton;
@ -41,7 +36,6 @@ struct _KatzePreferencesPrivate
GtkWidget* frame; GtkWidget* frame;
GtkWidget* box; GtkWidget* box;
GtkWidget* hbox; GtkWidget* hbox;
#endif
}; };
G_DEFINE_TYPE (KatzePreferences, katze_preferences, GTK_TYPE_DIALOG); G_DEFINE_TYPE (KatzePreferences, katze_preferences, GTK_TYPE_DIALOG);
@ -66,19 +60,6 @@ katze_preferences_response_cb (KatzePreferences* preferences,
gtk_widget_destroy (GTK_WIDGET (preferences)); gtk_widget_destroy (GTK_WIDGET (preferences));
} }
#ifdef HAVE_HILDON_2_2
static void
katze_preferences_size_request_cb (KatzePreferences* preferences,
GtkRequisition* requisition)
{
GdkScreen* screen = gtk_widget_get_screen (GTK_WIDGET (preferences));
if (gdk_screen_get_height (screen) > gdk_screen_get_width (screen))
gtk_widget_hide (gtk_dialog_get_action_area (GTK_DIALOG (preferences)));
else
gtk_widget_show (gtk_dialog_get_action_area (GTK_DIALOG (preferences)));
}
#endif
static void static void
katze_preferences_init (KatzePreferences* preferences) katze_preferences_init (KatzePreferences* preferences)
{ {
@ -103,18 +84,11 @@ katze_preferences_init (KatzePreferences* preferences)
gtk_dialog_add_buttons (GTK_DIALOG (preferences), gtk_dialog_add_buttons (GTK_DIALOG (preferences),
GTK_STOCK_HELP, GTK_RESPONSE_HELP, GTK_STOCK_HELP, GTK_RESPONSE_HELP,
NULL); NULL);
#if GTK_CHECK_VERSION (3, 0, 0) katze_widget_add_class (gtk_dialog_get_widget_for_response (
gtk_style_context_add_class (gtk_widget_get_style_context ( GTK_DIALOG (preferences), GTK_RESPONSE_HELP), "help_button");
gtk_dialog_get_widget_for_response (GTK_DIALOG (preferences),
GTK_RESPONSE_HELP)), "help_button");
#endif
gtk_dialog_add_buttons (GTK_DIALOG (preferences), gtk_dialog_add_buttons (GTK_DIALOG (preferences),
#if HAVE_HILDON
GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
#else
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
#endif
NULL); NULL);
#endif #endif
@ -122,12 +96,6 @@ katze_preferences_init (KatzePreferences* preferences)
"signal::response", katze_preferences_response_cb, NULL, "signal::response", katze_preferences_response_cb, NULL,
NULL); NULL);
#ifdef HAVE_HILDON_2_2
katze_preferences_size_request_cb (preferences, NULL);
g_object_connect (preferences,
"signal::size-request", katze_preferences_size_request_cb, NULL,
NULL);
#endif
} }
static void static void
@ -183,26 +151,12 @@ katze_preferences_prepare (KatzePreferences* preferences)
{ {
KatzePreferencesPrivate* priv = preferences->priv; KatzePreferencesPrivate* priv = preferences->priv;
#if HAVE_HILDON #ifdef HAVE_GRANITE
GtkWidget* viewport; /* FIXME: granite: should return GtkWidget* like GTK+ */
priv->notebook = (GtkWidget*)granite_widgets_static_notebook_new (FALSE);
priv->scrolled = katze_scrolled_new (NULL, NULL);
gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (preferences))),
priv->scrolled, TRUE, TRUE, 4);
viewport = gtk_viewport_new (NULL, NULL);
gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
gtk_container_add (GTK_CONTAINER (priv->scrolled), viewport);
priv->box = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (viewport), priv->box);
priv->hbox = NULL;
priv->sizegroup = NULL;
priv->sizegroup2 = NULL;
g_signal_connect (priv->scrolled, "destroy",
G_CALLBACK (gtk_widget_destroyed), &priv->scrolled);
#else #else
priv->notebook = gtk_notebook_new (); priv->notebook = gtk_notebook_new ();
#endif
gtk_container_set_border_width (GTK_CONTAINER (priv->notebook), 6); gtk_container_set_border_width (GTK_CONTAINER (priv->notebook), 6);
#if HAVE_OSX #if HAVE_OSX
@ -229,7 +183,6 @@ katze_preferences_prepare (KatzePreferences* preferences)
g_signal_connect (priv->notebook, "destroy", g_signal_connect (priv->notebook, "destroy",
G_CALLBACK (gtk_widget_destroyed), &priv->notebook); G_CALLBACK (gtk_widget_destroyed), &priv->notebook);
#endif
#if HAVE_OSX #if HAVE_OSX
GtkWidget* icon; GtkWidget* icon;
@ -271,24 +224,6 @@ katze_preferences_add_category (KatzePreferences* preferences,
priv = preferences->priv; priv = preferences->priv;
#if HAVE_HILDON
GtkWidget* widget;
gchar* markup;
if (!priv->scrolled)
katze_preferences_prepare (preferences);
widget = gtk_label_new (NULL);
gtk_widget_show (widget);
markup = g_markup_printf_escaped ("<b>%s</b>", label);
gtk_label_set_markup (GTK_LABEL (widget), markup);
g_free (markup);
gtk_box_pack_start (GTK_BOX (priv->box), widget, TRUE, TRUE, 0);
priv->sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
priv->sizegroup2 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
priv->hbox = NULL;
#else
if (!priv->notebook) if (!priv->notebook)
katze_preferences_prepare (preferences); katze_preferences_prepare (preferences);
@ -296,8 +231,14 @@ katze_preferences_add_category (KatzePreferences* preferences,
priv->sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); priv->sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
gtk_widget_show (priv->page); gtk_widget_show (priv->page);
gtk_container_set_border_width (GTK_CONTAINER (priv->page), 4); gtk_container_set_border_width (GTK_CONTAINER (priv->page), 4);
#ifdef HAVE_GRANITE
granite_widgets_static_notebook_append_page (
GRANITE_WIDGETS_STATIC_NOTEBOOK (priv->notebook),
priv->page, GTK_LABEL (gtk_label_new (label)));
#else
gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
priv->page, gtk_label_new (label)); priv->page, gtk_label_new (label));
#endif
#if HAVE_OSX #if HAVE_OSX
priv->toolbutton = GTK_WIDGET (priv->toolbutton ? priv->toolbutton = GTK_WIDGET (priv->toolbutton ?
gtk_radio_tool_button_new_from_widget ( gtk_radio_tool_button_new_from_widget (
@ -313,12 +254,10 @@ katze_preferences_add_category (KatzePreferences* preferences,
if (priv->toolbutton) if (priv->toolbutton)
g_object_set_data (G_OBJECT (priv->toolbutton), "notebook", priv->notebook); g_object_set_data (G_OBJECT (priv->toolbutton), "notebook", priv->notebook);
#endif #endif
#endif
return priv->page; return priv->page;
} }
#if !HAVE_HILDON
static GtkWidget* static GtkWidget*
katze_hig_frame_new (const gchar* title) katze_hig_frame_new (const gchar* title)
{ {
@ -336,7 +275,6 @@ katze_hig_frame_new (const gchar* title)
#endif #endif
return frame; return frame;
} }
#endif
/** /**
* katze_preferences_add_group: * katze_preferences_add_group:
@ -353,7 +291,6 @@ void
katze_preferences_add_group (KatzePreferences* preferences, katze_preferences_add_group (KatzePreferences* preferences,
const gchar* label) const gchar* label)
{ {
#if !HAVE_HILDON
KatzePreferencesPrivate* priv; KatzePreferencesPrivate* priv;
g_return_if_fail (KATZE_IS_PREFERENCES (preferences)); g_return_if_fail (KATZE_IS_PREFERENCES (preferences));
@ -368,7 +305,6 @@ katze_preferences_add_group (KatzePreferences* preferences,
gtk_container_set_border_width (GTK_CONTAINER (priv->box), 4); gtk_container_set_border_width (GTK_CONTAINER (priv->box), 4);
gtk_container_add (GTK_CONTAINER (priv->frame), priv->box); gtk_container_add (GTK_CONTAINER (priv->frame), priv->box);
gtk_widget_show_all (priv->frame); gtk_widget_show_all (priv->frame);
#endif
} }
/** /**
@ -402,10 +338,6 @@ katze_preferences_add_widget (KatzePreferences* preferences,
if (!priv->hbox) if (!priv->hbox)
_type = g_intern_string ("indented"); _type = g_intern_string ("indented");
#ifdef HAVE_HILDON_2_2
else if (HILDON_IS_CHECK_BUTTON (widget) || HILDON_IS_PICKER_BUTTON (widget))
_type = g_intern_string ("indented");
#endif
if (_type != g_intern_static_string ("spanned")) if (_type != g_intern_static_string ("spanned"))
{ {
@ -421,11 +353,7 @@ katze_preferences_add_widget (KatzePreferences* preferences,
GtkWidget* align = gtk_alignment_new (0, 0.5, 0, 0); GtkWidget* align = gtk_alignment_new (0, 0.5, 0, 0);
gtk_widget_show (align); gtk_widget_show (align);
gtk_container_add (GTK_CONTAINER (align), priv->hbox); gtk_container_add (GTK_CONTAINER (align), priv->hbox);
#if HAVE_HILDON
if (!GTK_IS_SPIN_BUTTON (widget) && !GTK_IS_LABEL (widget))
#else
if (!GTK_IS_SPIN_BUTTON (widget)) if (!GTK_IS_SPIN_BUTTON (widget))
#endif
gtk_size_group_add_widget (priv->sizegroup, widget); gtk_size_group_add_widget (priv->sizegroup, widget);
gtk_box_pack_start (GTK_BOX (priv->box), align, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (priv->box), align, TRUE, FALSE, 0);
} }
@ -439,9 +367,4 @@ katze_preferences_add_widget (KatzePreferences* preferences,
gtk_size_group_add_widget (priv->sizegroup2, widget); gtk_size_group_add_widget (priv->sizegroup2, widget);
gtk_box_pack_start (GTK_BOX (priv->hbox), align, TRUE, FALSE, 0); gtk_box_pack_start (GTK_BOX (priv->hbox), align, TRUE, FALSE, 0);
} }
#if HAVE_HILDON
if (GTK_IS_BUTTON (widget) && !GTK_WIDGET_IS_SENSITIVE (widget))
gtk_widget_hide (widget);
#endif
} }

View file

@ -1,906 +0,0 @@
/*
Copyright (C) 2007 Henrik Hedberg <hhedberg@innologies.fi>
Copyright (C) 2009 Nadav Wiener <nadavwr@yahoo.com>
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "katze-scrolled.h"
#include "katze-utils.h"
#define DEFAULT_INTERVAL 50
#define DEFAULT_DECELERATION 0.7
#define DEFAULT_DRAGGING_STOPPED_DELAY 100
/**
* SECTION:katze-scrolled
* @short_description: Implements drag scrolling and kinetic scrolling
* @see_also: #GtkScrolledWindow
*
* A scrolled window derived from #GtkScrolledWindow that implements
* drag scrolling and kinetic scrolling. Can be used as a drop-in replacement
* for the existing #GtkScrolledWindow.
*
* If a direct child of the #KatzeScrolled has its own window
* (InputOnly is enough for events), it is automatically activated when added
* as a child. All motion events in that area will be used to scroll.
*
* If some descendant widgets capture button press, button release and/ or
* motion nofity events, the user can not scroll the area by pressing those
* widgets (unless the widget is activated). #GtkButton is a typical example
* of that. Usually that is the desired behaviour.
*
* Any widget can be registered to provide pointer events for the
* #KatzeScrolled by using the
* #katze_scrolled_activate_scrolling function.
*
**/
G_DEFINE_TYPE (KatzeScrolled, katze_scrolled, GTK_TYPE_SCROLLED_WINDOW);
enum
{
PROP_0,
PROP_DRAG_SCROLLING,
PROP_KINETIC_SCROLLING
};
static void
katze_scrolled_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec);
static void
katze_scrolled_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec);
static void
katze_scrolled_dispose (GObject* object);
static void
katze_scrolled_activate_scrolling (KatzeScrolled* scrolled,
GtkWidget* widget);
static void
katze_scrolled_set_drag_scrolling (KatzeScrolled* scrolled,
gboolean drag_scrolling);
struct _KatzeScrolledPrivate
{
/* Settings */
guint interval;
gdouble deceleration;
gboolean drag_scrolling;
gboolean kinetic_scrolling;
guint32 dragging_stopped_delay;
gboolean scrolling_hints;
/* Temporary variables */
gboolean dragged;
gboolean press_received;
GdkWindow* synthetic_crossing_event_window;
/* Disabling twice happening scrolling adjustment */
GtkAdjustment* hadjustment;
GtkWidget* viewport;
/* Motion scrolling */
gint start_x;
gint start_y;
gint previous_x;
gint previous_y;
gint farest_x;
gint farest_y;
guint32 start_time;
guint32 previous_time;
guint32 farest_time;
gboolean going_right;
gboolean going_down;
/* Kinetic scrolling */
guint scrolling_timeout_id;
gdouble horizontal_speed;
gdouble vertical_speed;
gdouble horizontal_deceleration;
gdouble vertical_deceleration;
};
typedef struct _KatzeScrolledState KatzeScrolledState;
typedef gboolean (*KatzeScrolledEventHandler)(GdkEvent* event,
KatzeScrolledState* state,
gpointer user_data);
typedef struct
{
KatzeScrolledEventHandler event_handler;
gpointer user_data;
} EventHandlerData;
struct _KatzeScrolledState
{
GList* current_event_handler;
};
static GList* event_handlers = NULL;
static void
katze_scrolled_event_handler_func (GdkEvent* event,
gpointer data);
static void
katze_scrolled_event_handler_append (KatzeScrolledEventHandler event_handler,
gpointer user_data)
{
EventHandlerData* data;
data = g_new0 (EventHandlerData, 1);
data->event_handler = event_handler;
data->user_data = user_data;
event_handlers = g_list_append (event_handlers, data);
gdk_event_handler_set ((GdkEventFunc)katze_scrolled_event_handler_func, NULL, NULL);
}
static void
katze_scrolled_event_handler_next (GdkEvent* event,
KatzeScrolledState* state)
{
EventHandlerData* data;
gboolean stop_propagating;
state->current_event_handler = g_list_next (state->current_event_handler);
if (state->current_event_handler)
{
data = (EventHandlerData*)state->current_event_handler->data;
stop_propagating = data->event_handler (event, state, data->user_data);
if (!stop_propagating && state->current_event_handler)
g_critical ("%s: handler returned FALSE without calling %s first",
G_STRFUNC, G_STRFUNC);
}
else
gtk_main_do_event (event);
}
static void
katze_scrolled_event_handler_func (GdkEvent* event,
gpointer user_data)
{
KatzeScrolledState* state;
EventHandlerData* data;
gboolean stop_propagating;
state = g_slice_new (KatzeScrolledState);
state->current_event_handler = g_list_first (event_handlers);
if (state->current_event_handler)
{
data = (EventHandlerData*)state->current_event_handler->data;
stop_propagating = data->event_handler (event, state, data->user_data);
if (!stop_propagating && state->current_event_handler)
g_critical ("%s: handler returned FALSE without calling %s first",
G_STRFUNC, "katze_scrolled_event_handler_next");
}
else
gtk_main_do_event (event);
g_slice_free (KatzeScrolledState, state);
}
static GdkWindow* current_gdk_window;
static KatzeScrolled* current_scrolled_window;
static GtkWidget* current_widget;
static gboolean synthetized_crossing_event;
static GTree* activated_widgets;
static gint
compare_pointers (gconstpointer a,
gconstpointer b)
{
return a - b;
}
static void
disable_hadjustment (KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
GtkAdjustment* hadjustment;
GtkWidget* viewport;
if ((hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled)))
&& priv->hadjustment != hadjustment)
{
priv->hadjustment = hadjustment;
priv->viewport = NULL;
viewport = GTK_WIDGET (scrolled);
while (GTK_IS_BIN (viewport))
{
viewport = gtk_bin_get_child (GTK_BIN (viewport));
if (GTK_IS_VIEWPORT (viewport))
{
priv->viewport = viewport;
break;
}
}
}
g_signal_handlers_block_matched (priv->hadjustment, G_SIGNAL_MATCH_DATA,
0, 0, 0, 0, priv->viewport);
}
static void
enable_hadjustment (KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
g_signal_handlers_unblock_matched (priv->hadjustment, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, priv->viewport);
}
static gdouble
calculate_timeout_scroll_values (gdouble old_value,
gdouble upper_limit,
gdouble* scrolling_speed_pointer,
gdouble deceleration,
gdouble* other_deceleration,
gdouble normal_deceleration)
{
gdouble new_value = old_value;
if (*scrolling_speed_pointer > deceleration ||
*scrolling_speed_pointer < -deceleration)
{
if (old_value + *scrolling_speed_pointer <= 0.0)
{
new_value = -1.0;
*scrolling_speed_pointer = 0.0;
*other_deceleration = normal_deceleration;
}
else if (old_value + *scrolling_speed_pointer >= upper_limit)
{
new_value = upper_limit;
*scrolling_speed_pointer = 0.0;
*other_deceleration = normal_deceleration;
}
else
new_value = old_value + *scrolling_speed_pointer;
if (*scrolling_speed_pointer > deceleration)
*scrolling_speed_pointer -= deceleration;
else if (*scrolling_speed_pointer < -deceleration)
*scrolling_speed_pointer += deceleration;
}
return new_value;
}
static void
do_timeout_scroll (KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
GtkScrolledWindow* gtk_scrolled = GTK_SCROLLED_WINDOW (scrolled);
GtkAdjustment* hadjustment;
GtkAdjustment* vadjustment;
gdouble hpage_size, hupper, hvalue, new_hvalue;
gdouble vpage_size, vupper, vvalue, new_vvalue;
hadjustment = gtk_scrolled_window_get_hadjustment (gtk_scrolled);
hpage_size = gtk_adjustment_get_page_size (hadjustment);
hupper = gtk_adjustment_get_upper (hadjustment);
hvalue = gtk_adjustment_get_value (hadjustment);
new_hvalue = calculate_timeout_scroll_values (hvalue,
hupper - hpage_size,
&priv->horizontal_speed,
priv->horizontal_deceleration,
&priv->vertical_deceleration,
priv->deceleration);
vadjustment = gtk_scrolled_window_get_vadjustment (gtk_scrolled);
vpage_size = gtk_adjustment_get_page_size (vadjustment);
vupper = gtk_adjustment_get_upper (vadjustment);
vvalue = gtk_adjustment_get_value (vadjustment);
new_vvalue = calculate_timeout_scroll_values (vvalue,
vupper - vpage_size,
&priv->vertical_speed,
priv->vertical_deceleration,
&priv->horizontal_deceleration,
priv->deceleration);
if (new_vvalue != vvalue)
{
if (new_hvalue != hvalue)
{
disable_hadjustment (scrolled);
gtk_adjustment_set_value (hadjustment, new_hvalue);
enable_hadjustment (scrolled);
}
gtk_adjustment_set_value (vadjustment, new_vvalue);
}
else if (new_hvalue != hvalue)
gtk_adjustment_set_value (hadjustment, new_hvalue);
}
static gboolean
timeout_scroll (gpointer data)
{
gboolean ret = TRUE;
KatzeScrolled* scrolled = KATZE_SCROLLED (data);
KatzeScrolledPrivate* priv = scrolled->priv;
gdk_threads_enter ();
do_timeout_scroll (scrolled);
if (priv->vertical_speed < priv->deceleration &&
priv->vertical_speed > -priv->deceleration &&
priv->horizontal_speed < priv->deceleration &&
priv->horizontal_speed > -priv->deceleration)
{
priv->scrolling_timeout_id = 0;
ret = FALSE;
}
gdk_threads_leave ();
return ret;
}
static gdouble
calculate_motion_scroll_values (gdouble old_value,
gdouble upper_limit,
gint current_coordinate,
gint previous_coordinate)
{
gdouble new_value = old_value;
gint movement;
movement = current_coordinate - previous_coordinate;
if (old_value - movement < upper_limit)
new_value = old_value - movement;
else
new_value = upper_limit;
return new_value;
}
static void
do_motion_scroll (KatzeScrolled* scrolled,
GtkWidget* widget,
gint x,
gint y,
guint32 timestamp)
{
KatzeScrolledPrivate* priv = scrolled->priv;
if (priv->dragged || gtk_drag_check_threshold (widget, priv->start_x, priv->start_y, x, y))
{
GtkAdjustment* hadjustment;
GtkAdjustment* vadjustment;
gdouble hpage_size, hupper, hvalue, new_hvalue;
gdouble vpage_size, vupper, vvalue, new_vvalue;
if (timestamp - priv->previous_time > priv->dragging_stopped_delay || !priv->dragged)
{
priv->dragged = TRUE;
priv->going_right = priv->start_x < x;
priv->going_down = priv->start_y < y;
priv->start_x = priv->farest_x = x;
priv->start_y = priv->farest_y = y;
priv->start_time = priv->farest_time = timestamp;
}
else
{
if ((priv->going_right && x > priv->farest_x)
|| (!priv->going_right && x < priv->farest_x))
{
priv->farest_x = x;
priv->farest_time = timestamp;
}
if ((priv->going_down && y > priv->farest_y)
|| (!priv->going_down && y < priv->farest_y))
{
priv->farest_y = y;
priv->farest_time = timestamp;
}
if (gtk_drag_check_threshold (widget, priv->farest_x, priv->farest_y, x, y))
{
priv->start_x = priv->farest_x;
priv->farest_x = x;
priv->start_y = priv->farest_y;
priv->farest_y = y;
priv->start_time = priv->farest_time;
priv->farest_time = timestamp;
priv->going_right = priv->start_x < x;
priv->going_down = priv->start_y < y;
}
}
hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled));
hpage_size = gtk_adjustment_get_page_size (hadjustment);
hupper = gtk_adjustment_get_upper (hadjustment);
hvalue = gtk_adjustment_get_value (hadjustment);
new_hvalue = calculate_motion_scroll_values (hvalue,
hupper - hpage_size, x, priv->previous_x);
vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled));
vpage_size = gtk_adjustment_get_page_size (vadjustment);
vupper = gtk_adjustment_get_upper (vadjustment);
vvalue = gtk_adjustment_get_value (vadjustment);
new_vvalue = calculate_motion_scroll_values (vvalue,
vupper - vpage_size, y, priv->previous_y);
if (new_vvalue != vvalue)
{
if (new_hvalue != hvalue)
{
disable_hadjustment (scrolled);
gtk_adjustment_set_value (hadjustment, new_hvalue);
enable_hadjustment (scrolled);
}
gtk_adjustment_set_value (vadjustment, new_vvalue);
}
else if (new_hvalue != hvalue)
gtk_adjustment_set_value (hadjustment, new_hvalue);
}
priv->previous_y = y;
priv->previous_x = x;
priv->previous_time = timestamp;
}
static gboolean
button_press_event (GtkWidget* widget,
GdkEventButton* event,
KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
gint x;
gint y;
GdkModifierType mask;
if (!priv->drag_scrolling || event->button != 1)
return FALSE;
priv->press_received = TRUE;
gdk_window_get_pointer (gtk_widget_get_window (GTK_WIDGET (scrolled)),
&x, &y, &mask);
if (event->time - priv->previous_time < priv->dragging_stopped_delay &&
gtk_drag_check_threshold (widget, priv->previous_x, priv->previous_y, x, y))
{
if (priv->scrolling_timeout_id)
{
g_source_remove (priv->scrolling_timeout_id);
priv->scrolling_timeout_id = 0;
}
/* do_motion_scroll (scrolled, widget, x, y, event->time); */
}
else
{
if (priv->scrolling_timeout_id)
{
g_source_remove (priv->scrolling_timeout_id);
priv->scrolling_timeout_id = 0;
priv->previous_time = 0;
}
else
{
priv->dragged = FALSE;
priv->previous_time = event->time;
}
priv->start_x = priv->previous_x = priv->farest_x = x;
priv->start_y = priv->previous_y = priv->farest_y = y;
priv->start_time = event->time;
}
return FALSE;
}
static gboolean
button_release_event (GtkWidget* widget,
GdkEventButton* event,
KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
gint x;
gint y;
GdkModifierType mask;
gdk_window_get_pointer (gtk_widget_get_window (GTK_WIDGET (scrolled)),
&x, &y, &mask);
if (priv->press_received &&
gtk_drag_check_threshold (widget, priv->start_x, priv->start_y, x, y)) {
priv->dragged = TRUE;
}
if (priv->press_received && priv->kinetic_scrolling &&
event->time - priv->previous_time < priv->dragging_stopped_delay) {
priv->vertical_speed = (gdouble)(priv->start_y - y) / (event->time - priv->start_time) * priv->interval;
priv->horizontal_speed = (gdouble)(priv->start_x - x) / (event->time - priv->start_time) * priv->interval;
if (ABS (priv->vertical_speed) > ABS (priv->horizontal_speed)) {
priv->vertical_deceleration = priv->deceleration;
priv->horizontal_deceleration = priv->deceleration * ABS (priv->horizontal_speed / priv->vertical_speed);
} else {
priv->horizontal_deceleration = priv->deceleration;
priv->vertical_deceleration = priv->deceleration * ABS (priv->vertical_speed / priv->horizontal_speed);
}
priv->scrolling_timeout_id = g_timeout_add (priv->interval, timeout_scroll, scrolled);
do_timeout_scroll (scrolled);
}
priv->previous_x = x;
priv->previous_y = y;
priv->previous_time = event->time;
priv->press_received = FALSE;
return FALSE;
}
static gboolean
motion_notify_event (GtkWidget* widget,
GdkEventMotion* event,
KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv = scrolled->priv;
gint x;
gint y;
GdkModifierType mask;
if (priv->press_received)
{
gdk_window_get_pointer (gtk_widget_get_window (GTK_WIDGET (scrolled)),
&x, &y, &mask);
do_motion_scroll (scrolled, widget, x, y, event->time);
}
return FALSE;
}
static gboolean
katze_scrolled_event_handler (GdkEvent* event,
KatzeScrolledState* state,
gpointer user_data)
{
gboolean stop_propagating;
GdkEventCrossing crossing;
stop_propagating = FALSE;
if (event->type == GDK_BUTTON_PRESS)
{
gdk_window_get_user_data (event->button.window, (gpointer)&current_widget);
if ((current_scrolled_window = g_tree_lookup (activated_widgets, current_widget)))
{
current_gdk_window = event->button.window;
stop_propagating = button_press_event (current_widget, &event->button, current_scrolled_window);
}
else
current_gdk_window = NULL;
}
else if (event->any.window == current_gdk_window)
{
if (event->type == GDK_MOTION_NOTIFY)
{
if (current_scrolled_window->priv->dragged)
stop_propagating = motion_notify_event (current_widget, &event->motion, current_scrolled_window);
else
{
stop_propagating = motion_notify_event (current_widget, &event->motion, current_scrolled_window);
if (current_scrolled_window->priv->dragged)
{
crossing.type = GDK_LEAVE_NOTIFY;
crossing.window = event->motion.window;
crossing.send_event = event->motion.send_event;
crossing.subwindow = gtk_widget_get_window (
GTK_WIDGET (current_scrolled_window));
crossing.time = event->motion.time;
crossing.x = event->motion.x;
crossing.y = event->motion.y;
crossing.x_root = event->motion.x_root;
crossing.y_root = event->motion.y_root;
crossing.mode = GDK_CROSSING_GRAB;
crossing.detail = GDK_NOTIFY_ANCESTOR;
crossing.focus = TRUE;
crossing.state = event->motion.state;
gtk_main_do_event ((GdkEvent*)&crossing);
synthetized_crossing_event = TRUE;
}
}
}
else if ((event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) &&
synthetized_crossing_event)
stop_propagating = TRUE;
else if (event->type == GDK_BUTTON_RELEASE)
stop_propagating = button_release_event (current_widget, &event->button, current_scrolled_window);
}
if (!stop_propagating)
katze_scrolled_event_handler_next (event, state);
if (event->type == GDK_BUTTON_RELEASE && event->button.window == current_gdk_window)
{
crossing.type = GDK_ENTER_NOTIFY;
crossing.window = event->button.window;
crossing.send_event = event->button.send_event;
crossing.subwindow = gtk_widget_get_window (
GTK_WIDGET (current_scrolled_window));
crossing.time = event->button.time;
crossing.x = event->button.x;
crossing.y = event->button.y;
crossing.x_root = event->button.x_root;
crossing.y_root = event->button.y_root;
crossing.mode = GDK_CROSSING_UNGRAB;
crossing.detail = GDK_NOTIFY_ANCESTOR;
crossing.focus = TRUE;
crossing.state = event->button.state;
gtk_main_do_event ((GdkEvent*)&crossing);
synthetized_crossing_event = FALSE;
}
return stop_propagating;
}
static void
katze_scrolled_add (GtkContainer* container,
GtkWidget* widget)
{
katze_scrolled_activate_scrolling (KATZE_SCROLLED (container), widget);
(*GTK_CONTAINER_CLASS (katze_scrolled_parent_class)->add) (container, widget);
}
static void
katze_scrolled_realize (GtkWidget* widget)
{
KatzeScrolled* scrolled = KATZE_SCROLLED (widget);
gboolean drag_scrolling;
GtkPolicyType policy;
GdkWindow* window;
GdkWindowAttr attr;
(*GTK_WIDGET_CLASS (katze_scrolled_parent_class)->realize) (widget);
drag_scrolling = katze_widget_has_touchscreen_mode (widget);
policy = drag_scrolling ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC;
g_object_set (scrolled, "drag-scrolling", drag_scrolling,
"hscrollbar-policy", policy, "vscrollbar-policy", policy, NULL);
window = g_object_ref (gtk_widget_get_parent_window (widget));
gtk_widget_set_window (widget, window);
attr.height = attr.width = 10;
attr.event_mask = GDK_EXPOSURE_MASK;
attr.wclass = GDK_INPUT_OUTPUT;
attr.window_type = GDK_WINDOW_CHILD;
attr.override_redirect = TRUE;
gtk_widget_set_realized (widget, TRUE);
}
static void
katze_scrolled_dispose (GObject* object)
{
KatzeScrolled* scrolled = KATZE_SCROLLED (object);
KatzeScrolledPrivate* priv = scrolled->priv;
if (priv->scrolling_timeout_id)
{
g_source_remove (priv->scrolling_timeout_id);
priv->scrolling_timeout_id = 0;
}
(*G_OBJECT_CLASS (katze_scrolled_parent_class)->dispose) (object);
}
static void
katze_scrolled_class_init (KatzeScrolledClass* class)
{
GObjectClass* gobject_class;
GtkWidgetClass* widget_class;
GtkContainerClass* container_class;
GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
gobject_class = G_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
container_class = GTK_CONTAINER_CLASS (class);
gobject_class->set_property = katze_scrolled_set_property;
gobject_class->get_property = katze_scrolled_get_property;
gobject_class->dispose = katze_scrolled_dispose;
widget_class->realize = katze_scrolled_realize;
container_class->add = katze_scrolled_add;
/**
* KatzeScrolled:drag-scrolling:
*
* Whether the widget can be scrolled by dragging its contents.
*
* If "gtk-touchscreen-mode" is enabled, drag scrolling is
* automatically enabled.
*
* Since: 0.2.0
*/
g_object_class_install_property (gobject_class,
PROP_DRAG_SCROLLING,
g_param_spec_boolean (
"drag-scrolling",
"Drag Scrolling",
"Whether the widget can be scrolled by dragging its contents",
FALSE,
flags));
/**
* KatzeScrolled:kinetic-scrolling:
*
* Whether drag scrolling is kinetic, that is releasing the
* pointer keeps the contents scrolling further relative to
* the speed with which they were dragged.
*
* Since: 0.2.0
*/
g_object_class_install_property (gobject_class,
PROP_KINETIC_SCROLLING,
g_param_spec_boolean (
"kinetic-scrolling",
"Kinetic Scrolling",
"Whether drag scrolling is kinetic",
TRUE,
flags));
activated_widgets = g_tree_new ((GCompareFunc)compare_pointers);
current_gdk_window = NULL;
/* Usually touchscreen mode is either always set or it isn't, so it
should be a safe optimization to not setup events if not needed. */
if (katze_widget_has_touchscreen_mode (NULL))
katze_scrolled_event_handler_append (katze_scrolled_event_handler, NULL);
g_type_class_add_private (class, sizeof (KatzeScrolledPrivate));
}
static void
katze_scrolled_init (KatzeScrolled* scrolled)
{
KatzeScrolledPrivate* priv;
scrolled->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE ((scrolled),
KATZE_TYPE_SCROLLED, KatzeScrolledPrivate);
priv->interval = DEFAULT_INTERVAL;
priv->deceleration = DEFAULT_DECELERATION;
priv->drag_scrolling = FALSE;
priv->kinetic_scrolling = TRUE;
priv->dragging_stopped_delay = DEFAULT_DRAGGING_STOPPED_DELAY;
}
static void
katze_scrolled_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec)
{
KatzeScrolled* scrolled = KATZE_SCROLLED (object);
switch (prop_id)
{
case PROP_DRAG_SCROLLING:
katze_scrolled_set_drag_scrolling (scrolled, g_value_get_boolean (value));
break;
case PROP_KINETIC_SCROLLING:
scrolled->priv->kinetic_scrolling = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
katze_scrolled_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec)
{
KatzeScrolled* scrolled = KATZE_SCROLLED (object);
switch (prop_id)
{
case PROP_DRAG_SCROLLING:
g_value_set_boolean (value, scrolled->priv->drag_scrolling);
break;
case PROP_KINETIC_SCROLLING:
g_value_set_boolean (value, scrolled->priv->kinetic_scrolling);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* katze_scrolled_new:
* @hadjustment: a horizontal #GtkAdjustment, or %NULL
* @vadjustment: a vertical #GtkAdjustment, or %NULL
*
* Creates a new #KatzeScrolled.
*
* Since: 0.2.0
**/
GtkWidget*
katze_scrolled_new (GtkAdjustment* hadjustment,
GtkAdjustment* vadjustment)
{
if (hadjustment)
g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
if (vadjustment)
g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
return gtk_widget_new (KATZE_TYPE_SCROLLED,
"hadjustment", hadjustment,
"vadjustment", vadjustment, NULL);
}
/**
* katze_scrolled_activate_scrolling:
* @scrolled: a #KatzeScrolled
* @widget: a #GtkWidget of which area is made active event source for
* drag and kinetic scrolling.
*
* Activates the widget so that pointer motion events inside the widget are
* used to scroll the #KatzeScrolled. The widget can be a child of the
* #KatzeScrolled or even a separate widget ("touchpad" style).
*
* The direct child of the #KatzeScrolled (typically #GtkViewport) is
* activated automatically when added. This function has to be used if indirect
* descendant widgets are stopping propagation of the button press and release
* as well as motion events (for example GtkButton is doing so) but scrolling
* should be possible inside their area too.
*
* This function adds #GDK_BUTTON_PRESS_MASK, #GDK_BUTTON_RELEASE_MASK,
* #GDK_POINTER_MOTION_MASK, and #GDK_MOTION_HINT_MAKS into the widgets event mask.
*/
static void
katze_scrolled_activate_scrolling (KatzeScrolled* scrolled,
GtkWidget* widget)
{
gtk_widget_add_events (widget,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
g_tree_insert (activated_widgets, widget, scrolled);
}
static void
katze_scrolled_set_drag_scrolling (KatzeScrolled* scrolled,
gboolean drag_scrolling)
{
KatzeScrolledPrivate* priv = scrolled->priv;
if (priv->drag_scrolling && !drag_scrolling)
{
if (priv->scrolling_timeout_id)
{
g_source_remove (priv->scrolling_timeout_id);
priv->scrolling_timeout_id = 0;
priv->previous_time = 0;
}
priv->press_received = FALSE;
}
priv->drag_scrolling = drag_scrolling;
}

View file

@ -1,59 +0,0 @@
/*
Copyright (C) 2007 Henrik Hedberg <hhedberg@innologies.fi>
Copyright (C) 2009 Nadav Wiener <nadavwr@yahoo.com>
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
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.
*/
#ifndef KATZE_SCROLLED_H
#define KATZE_SCROLLED_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define KATZE_TYPE_SCROLLED (katze_scrolled_get_type())
#define KATZE_SCROLLED(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), KATZE_TYPE_SCROLLED, KatzeScrolled))
#define KATZE_SCROLLED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), KATZE_TYPE_SCROLLED, KatzeScrolledClass))
#define KATZE_IS_SCROLLED(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KATZE_TYPE_SCROLLED))
#define KATZE_IS_SCROLLED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), KATZE_TYPE_SCROLLED))
#define KATZE_SCROLLED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), KATZE_TYPE_SCROLLED, KatzeScrolledClass))
typedef struct _KatzeScrolled KatzeScrolled;
typedef struct _KatzeScrolledClass KatzeScrolledClass;
typedef struct _KatzeScrolledPrivate KatzeScrolledPrivate;
struct _KatzeScrolled
{
GtkScrolledWindow parent;
KatzeScrolledPrivate* priv;
};
struct _KatzeScrolledClass
{
GtkScrolledWindowClass parent;
/* Padding for future expansion */
void (*_katze_reserved1) (void);
void (*_katze_reserved2) (void);
void (*_katze_reserved3) (void);
void (*_katze_reserved4) (void);
};
GType
katze_scrolled_get_type (void) G_GNUC_CONST;
GtkWidget*
katze_scrolled_new (GtkAdjustment* hadjustment,
GtkAdjustment* vadjustment);
G_END_DECLS
#endif /* __KATZE_SCROLLED_H__ */

View file

@ -1,134 +0,0 @@
/*
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
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.
*/
#include "katze-separatoraction.h"
struct _KatzeSeparatorAction
{
GtkAction parent_instance;
};
struct _KatzeSeparatorActionClass
{
GtkActionClass parent_class;
};
G_DEFINE_TYPE (KatzeSeparatorAction, katze_separator_action, GTK_TYPE_ACTION);
static void
katze_separator_action_finalize (GObject* object);
static void
katze_separator_action_activate (GtkAction* object);
static GtkWidget*
katze_separator_action_create_tool_item (GtkAction* action);
static GtkWidget*
katze_separator_action_create_menu_item (GtkAction* action);
static void
katze_separator_action_connect_proxy (GtkAction* action,
GtkWidget* proxy);
static void
katze_separator_action_disconnect_proxy (GtkAction* action,
GtkWidget* proxy);
static void
katze_separator_action_class_init (KatzeSeparatorActionClass* class)
{
GObjectClass* gobject_class;
GtkActionClass* action_class;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = katze_separator_action_finalize;
action_class = GTK_ACTION_CLASS (class);
action_class->activate = katze_separator_action_activate;
action_class->create_menu_item = katze_separator_action_create_menu_item;
action_class->create_tool_item = katze_separator_action_create_tool_item;
action_class->connect_proxy = katze_separator_action_connect_proxy;
action_class->disconnect_proxy = katze_separator_action_disconnect_proxy;
}
static void
katze_separator_action_init (KatzeSeparatorAction* separator_action)
{
/* Nothing to do. */
}
static void
katze_separator_action_finalize (GObject* object)
{
G_OBJECT_CLASS (katze_separator_action_parent_class)->finalize (object);
}
static void
katze_separator_action_activate (GtkAction* action)
{
GSList* proxies;
proxies = gtk_action_get_proxies (action);
if (!proxies)
return;
do
if (GTK_IS_TOOL_ITEM (proxies->data))
{
}
while ((proxies = g_slist_next (proxies)));
if (GTK_ACTION_CLASS (katze_separator_action_parent_class)->activate)
GTK_ACTION_CLASS (katze_separator_action_parent_class)->activate (action);
}
static GtkWidget*
katze_separator_action_create_menu_item (GtkAction* action)
{
GtkWidget* menuitem;
menuitem = gtk_separator_menu_item_new ();
return menuitem;
}
static GtkWidget*
katze_separator_action_create_tool_item (GtkAction* action)
{
GtkWidget* toolitem;
toolitem = GTK_WIDGET (gtk_separator_tool_item_new ());
return toolitem;
}
static void
katze_separator_action_connect_proxy (GtkAction* action,
GtkWidget* proxy)
{
GTK_ACTION_CLASS (katze_separator_action_parent_class)->connect_proxy (
action, proxy);
if (GTK_IS_TOOL_ITEM (proxy))
{
}
else if (GTK_IS_MENU_ITEM (proxy))
{
}
}
static void
katze_separator_action_disconnect_proxy (GtkAction* action,
GtkWidget* proxy)
{
GTK_ACTION_CLASS (katze_separator_action_parent_class)->disconnect_proxy
(action, proxy);
}

View file

@ -1,43 +0,0 @@
/*
Copyright (C) 2009 Christian Dywan <christian@twotoasts.de>
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.
*/
#ifndef __KATZE_SEPARATOR_ACTION_H__
#define __KATZE_SEPARATOR_ACTION_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define KATZE_TYPE_SEPARATOR_ACTION \
(katze_separator_action_get_type ())
#define KATZE_SEPARATOR_ACTION(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), KATZE_TYPE_SEPARATOR_ACTION, \
KatzeSeparatorAction))
#define KATZE_SEPARATOR_ACTION_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), KATZE_TYPE_SEPARATOR_ACTION, \
KatzeSeparatorActionClass))
#define KATZE_IS_SEPARATOR_ACTION(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), KATZE_TYPE_SEPARATOR_ACTION))
#define KATZE_IS_SEPARATOR_ACTION_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), KATZE_TYPE_SEPARATOR_ACTION))
#define KATZE_SEPARATOR_ACTION_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), KATZE_TYPE_SEPARATOR_ACTION, \
KatzeSeparatorActionClass))
typedef struct _KatzeSeparatorAction KatzeSeparatorAction;
typedef struct _KatzeSeparatorActionClass KatzeSeparatorActionClass;
GType
katze_separator_action_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __KATZE_SEPARATOR_ACTION_H__ */

View file

@ -0,0 +1,28 @@
/*
Copyright (C) 2009-2013 Christian Dywan <christian@twotoasts.de>
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 Katze {
public class SeparatorAction : Gtk.Action {
Gtk.MenuItem? menuitem = null;
Gtk.ToolItem? toolitem = null;
public override unowned Gtk.Widget create_menu_item () {
menuitem = new Gtk.SeparatorMenuItem ();
return menuitem;
}
public override unowned Gtk.Widget create_tool_item () {
toolitem = new Gtk.SeparatorToolItem ();
return toolitem;
}
}
}

View file

@ -97,8 +97,8 @@ katze_throbber_realize (GtkWidget* widget);
#if GTK_CHECK_VERSION (3, 0, 0) #if GTK_CHECK_VERSION (3, 0, 0)
static void static void
katze_throbber_get_preferred_height (GtkWidget *widget, katze_throbber_get_preferred_height (GtkWidget *widget,
gint *minimal_width, gint *minimal_height,
gint *natural_width); gint *natural_height);
static void static void
katze_throbber_get_preferred_width (GtkWidget *widget, katze_throbber_get_preferred_width (GtkWidget *widget,
gint *minimal_width, gint *minimal_width,
@ -229,7 +229,7 @@ katze_throbber_class_init (KatzeThrobberClass* class)
flags)); flags));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_PIXBUF, PROP_STATIC_PIXBUF,
g_param_spec_object ( g_param_spec_object (
"static-pixbuf", "static-pixbuf",
"Static Pixbuf", "Static Pixbuf",
@ -490,13 +490,11 @@ katze_throbber_set_animated (KatzeThrobber* throbber,
g_object_set (throbber, "active", animated, NULL); g_object_set (throbber, "active", animated, NULL);
#else #else
if (animated && (throbber->timer_id < 0)) if (animated && (throbber->timer_id < 0))
throbber->timer_id = g_timeout_add_full ( throbber->timer_id = midori_timeout_add (50,
G_PRIORITY_LOW, 50, (GSourceFunc)katze_throbber_timeout, throbber,
(GSourceFunc)katze_throbber_timeout,
throbber,
(GDestroyNotify)katze_throbber_timeout_destroy); (GDestroyNotify)katze_throbber_timeout_destroy);
gtk_widget_queue_draw (GTK_WIDGET (throbber));
#endif #endif
gtk_widget_queue_draw (GTK_WIDGET (throbber));
g_object_notify (G_OBJECT (throbber), "animated"); g_object_notify (G_OBJECT (throbber), "animated");
} }
@ -857,14 +855,14 @@ katze_throbber_size_request (GtkWidget* widget,
#if GTK_CHECK_VERSION (3, 0, 0) #if GTK_CHECK_VERSION (3, 0, 0)
static void static void
katze_throbber_get_preferred_height (GtkWidget *widget, katze_throbber_get_preferred_height (GtkWidget *widget,
gint *minimal_width, gint *minimal_height,
gint *natural_width) gint *natural_height)
{ {
GtkRequisition requisition; GtkRequisition requisition;
katze_throbber_size_request (widget, &requisition); katze_throbber_size_request (widget, &requisition);
*minimal_width = *natural_width = requisition.height; *minimal_height = *natural_height = requisition.height;
} }
static void static void
@ -902,6 +900,7 @@ katze_throbber_aligned_coords (GtkWidget* widget,
#endif #endif
#if GTK_CHECK_VERSION (3, 0, 0) #if GTK_CHECK_VERSION (3, 0, 0)
allocation.x = allocation.y = 0;
allocation.width = gtk_widget_get_allocated_width (widget); allocation.width = gtk_widget_get_allocated_width (widget);
allocation.height = gtk_widget_get_allocated_height (widget); allocation.height = gtk_widget_get_allocated_height (widget);
gtk_widget_get_preferred_size (widget, &requisition, NULL); gtk_widget_get_preferred_size (widget, &requisition, NULL);

View file

@ -29,10 +29,6 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef HAVE_HILDON_2_2
#include <hildon/hildon.h>
#endif
#define I_ g_intern_static_string #define I_ g_intern_static_string
static void static void
@ -42,12 +38,7 @@ proxy_toggle_button_toggled_cb (GtkToggleButton* button,
gboolean toggled; gboolean toggled;
const gchar* property; const gchar* property;
#ifdef HAVE_HILDON_2_2
if (HILDON_IS_CHECK_BUTTON (button))
toggled = hildon_check_button_get_active (HILDON_CHECK_BUTTON (button));
#else
toggled = gtk_toggle_button_get_active (button); toggled = gtk_toggle_button_get_active (button);
#endif
property = g_object_get_data (G_OBJECT (button), "property"); property = g_object_get_data (G_OBJECT (button), "property");
g_object_set (object, property, toggled, NULL); g_object_set (object, property, toggled, NULL);
} }
@ -79,6 +70,24 @@ proxy_uri_file_set_cb (GtkFileChooser* button,
g_object_set (object, property, file, NULL); g_object_set (object, property, file, NULL);
} }
#if GTK_CHECK_VERSION (3, 2, 0)
static void
proxy_font_chooser_font_activated_cb (GtkFontChooser* chooser,
const gchar* font_name,
GObject* object)
{
gtk_font_chooser_set_font (chooser, font_name);
}
static gboolean
proxy_font_chooser_filter_monospace_cb (PangoFontFamily* family,
PangoFontFace* face,
gpointer data)
{
gboolean monospace = GPOINTER_TO_INT (data);
return monospace == pango_font_family_is_monospace (family);
}
#else
static void static void
proxy_combo_box_text_changed_cb (GtkComboBoxText* button, proxy_combo_box_text_changed_cb (GtkComboBoxText* button,
GObject* object) GObject* object)
@ -88,6 +97,7 @@ proxy_combo_box_text_changed_cb (GtkComboBoxText* button,
g_object_set (object, property, text, NULL); g_object_set (object, property, text, NULL);
g_free (text); g_free (text);
} }
#endif
static const gchar* static const gchar*
katze_app_info_get_commandline (GAppInfo* info) katze_app_info_get_commandline (GAppInfo* info)
@ -219,17 +229,6 @@ proxy_spin_button_changed_cb (GtkSpinButton* button,
} }
} }
#ifdef HAVE_HILDON_2_2
static void
proxy_picker_button_changed_cb (HildonPickerButton* button,
GObject* object)
{
gint value = hildon_picker_button_get_active (button);
const gchar* property = g_object_get_data (G_OBJECT (button), "property");
g_object_set (object, property, value, NULL);
/* FIXME: Implement custom-PROPERTY */
}
#else
static void static void
proxy_combo_box_changed_cb (GtkComboBox* button, proxy_combo_box_changed_cb (GtkComboBox* button,
GObject* object) GObject* object)
@ -277,7 +276,6 @@ proxy_combo_box_changed_cb (GtkComboBox* button,
if (custom_value) if (custom_value)
{ {
#if GTK_CHECK_VERSION (2, 12, 0)
if (value == custom_value) if (value == custom_value)
gtk_widget_set_tooltip_text (GTK_WIDGET (button), NULL); gtk_widget_set_tooltip_text (GTK_WIDGET (button), NULL);
else else
@ -286,10 +284,8 @@ proxy_combo_box_changed_cb (GtkComboBox* button,
gtk_widget_set_tooltip_text (GTK_WIDGET (button), custom_text); gtk_widget_set_tooltip_text (GTK_WIDGET (button), custom_text);
g_free (custom_text); g_free (custom_text);
} }
#endif
} }
} }
#endif
static void static void
proxy_object_notify_boolean_cb (GObject* object, proxy_object_notify_boolean_cb (GObject* object,
@ -331,12 +327,19 @@ proxy_widget_string_destroy_cb (GtkWidget* proxy,
static GList* static GList*
katze_app_info_get_all_for_category (const gchar* category) katze_app_info_get_all_for_category (const gchar* category)
{ {
#ifdef _WIN32
/* FIXME: Real filtering by category would be better */
const gchar* content_type = g_content_type_from_mime_type (category);
GList* all_apps = g_app_info_get_all_for_type (content_type);
#else
GList* all_apps = g_app_info_get_all (); GList* all_apps = g_app_info_get_all ();
#endif
GList* apps = NULL; GList* apps = NULL;
guint i = 0;
GAppInfo* info; GAppInfo* info;
while ((info = g_list_nth_data (all_apps, i++))) GList* app;
for (app = apps; app; app = g_list_next (app))
{ {
GAppInfo* info = app->data;
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
gchar* filename = g_strconcat ("applications/", g_app_info_get_id (info), NULL); gchar* filename = g_strconcat ("applications/", g_app_info_get_id (info), NULL);
GKeyFile* file = g_key_file_new (); GKeyFile* file = g_key_file_new ();
@ -360,6 +363,92 @@ katze_app_info_get_all_for_category (const gchar* category)
return apps; return apps;
} }
static gboolean
proxy_populate_apps (GtkWidget* widget)
{
const gchar* property = g_object_get_data (G_OBJECT (widget), "property");
GObject* object = g_object_get_data (G_OBJECT (widget), "object");
gchar* string = katze_object_get_string (object, property);
if (!g_strcmp0 (string, ""))
katze_assign (string, NULL);
GtkSettings* settings = gtk_widget_get_settings (widget);
gint icon_width = 16;
if (settings == NULL)
settings = gtk_settings_get_for_screen (gdk_screen_get_default ());
gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
&icon_width, NULL);
GtkComboBox* combo = GTK_COMBO_BOX (widget);
GtkListStore* model = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
GtkTreeIter iter_none;
gtk_list_store_insert_with_values (model, &iter_none, 0,
0, NULL, 1, NULL, 2, _("None"), 3, icon_width, -1);
const gchar* app_type = g_object_get_data (G_OBJECT (widget), "app-type");
GList* apps = g_app_info_get_all_for_type (app_type);
GAppInfo* info;
if (!apps)
apps = katze_app_info_get_all_for_category (app_type);
if (apps != NULL)
{
GList* app;
for (app = apps; app; app = g_list_next (app))
{
GAppInfo* info = app->data;
const gchar* name = g_app_info_get_name (info);
GIcon* icon = g_app_info_get_icon (info);
gchar* icon_name;
GtkTreeIter iter;
if (!g_app_info_should_show (info))
continue;
icon_name = icon ? g_icon_to_string (icon) : NULL;
gtk_list_store_insert_with_values (model, &iter, G_MAXINT,
0, info, 1, icon_name, 2, name, 3, icon_width, -1);
if (string && !strcmp (katze_app_info_get_commandline (info), string))
gtk_combo_box_set_active_iter (combo, &iter);
g_free (icon_name);
}
g_list_free (apps);
}
info = g_app_info_create_from_commandline ("",
"", G_APP_INFO_CREATE_NONE, NULL);
gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
0, info, 1, NULL, 2, _("Custom…"), 3, icon_width, -1);
g_object_unref (info);
if (gtk_combo_box_get_active (combo) == -1)
{
if (string)
{
GtkWidget* entry;
const gchar* exe;
info = g_app_info_create_from_commandline (string,
NULL, G_APP_INFO_CREATE_NONE, NULL);
entry = gtk_entry_new ();
exe = g_app_info_get_executable (info);
if (exe && *exe && strcmp (exe, "%f"))
gtk_entry_set_text (GTK_ENTRY (entry), string);
gtk_widget_show (entry);
gtk_container_add (GTK_CONTAINER (combo), entry);
g_object_unref (info);
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_entry_focus_out_event_cb), object);
g_object_set_data_full (G_OBJECT (entry), "property",
g_strdup (property), g_free);
}
else
gtk_combo_box_set_active_iter (combo, &iter_none);
}
g_signal_connect (widget, "changed",
G_CALLBACK (proxy_combo_box_apps_changed_cb), object);
return G_SOURCE_REMOVE;
}
/** /**
* katze_property_proxy: * katze_property_proxy:
* @object: a #GObject * @object: a #GObject
@ -452,23 +541,14 @@ katze_property_proxy (gpointer object,
gchar* notify_property; gchar* notify_property;
gboolean toggled = katze_object_get_boolean (object, property); gboolean toggled = katze_object_get_boolean (object, property);
#ifdef HAVE_HILDON_2_2
if (_hint != I_("toggle"))
{
widget = hildon_check_button_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
gtk_button_set_label (GTK_BUTTON (widget), gettext (nick));
hildon_check_button_set_active (HILDON_CHECK_BUTTON (widget), toggled);
}
else
#endif
{
widget = gtk_check_button_new (); widget = gtk_check_button_new ();
if (_hint == I_("toggle")) if (_hint == I_("toggle"))
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (widget), FALSE); gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (widget), FALSE);
else else
gtk_button_set_label (GTK_BUTTON (widget), gettext (nick)); gtk_button_set_label (GTK_BUTTON (widget), gettext (nick));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), toggled); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), toggled);
}
g_signal_connect (widget, "toggled", g_signal_connect (widget, "toggled",
G_CALLBACK (proxy_toggle_button_toggled_cb), object); G_CALLBACK (proxy_toggle_button_toggled_cb), object);
notify_property = g_strdup_printf ("notify::%s", property); notify_property = g_strdup_printf ("notify::%s", property);
@ -517,42 +597,46 @@ katze_property_proxy (gpointer object,
string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value); string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (widget), gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (widget),
string ? string : ""); string ? string : "");
#if GTK_CHECK_VERSION (2, 12, 0)
g_signal_connect (widget, "file-set", g_signal_connect (widget, "file-set",
G_CALLBACK (proxy_uri_file_set_cb), object); G_CALLBACK (proxy_uri_file_set_cb), object);
#else
if (pspec->flags & G_PARAM_WRITABLE)
g_signal_connect (widget, "selection-changed",
G_CALLBACK (proxy_uri_file_set_cb), object);
#endif
} }
else if (type == G_TYPE_PARAM_STRING && (_hint == I_("font") else if (type == G_TYPE_PARAM_STRING && (_hint == I_("font")
|| _hint == I_("font-monospace"))) || _hint == I_("font-monospace")))
{ {
GtkComboBox* combo;
gint n_families, i;
PangoContext* context;
PangoFontFamily** families;
gboolean monospace = _hint == I_("font-monospace");
string = katze_object_get_string (object, property); string = katze_object_get_string (object, property);
widget = gtk_combo_box_text_new ();
combo = GTK_COMBO_BOX (widget);
context = gtk_widget_get_pango_context (widget);
pango_context_list_families (context, &families, &n_families);
if (!string) if (!string)
string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value); string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
/* 'sans' and 'sans-serif' are presumably the same */ /* 'sans' and 'sans-serif' are presumably the same */
if (!g_strcmp0 (string, "sans-serif")) if (!g_strcmp0 (string, "sans-serif"))
katze_assign (string, g_strdup ("sans")); katze_assign (string, g_strdup ("sans"));
gboolean monospace = _hint == I_("font-monospace");
#if GTK_CHECK_VERSION (3, 2, 0)
widget = gtk_font_button_new ();
gtk_font_button_set_show_size (GTK_FONT_BUTTON (widget), FALSE);
gtk_font_chooser_set_font (GTK_FONT_CHOOSER (widget), string);
g_signal_connect (widget, "font-activated",
G_CALLBACK (proxy_font_chooser_font_activated_cb), object);
gtk_font_chooser_set_filter_func (GTK_FONT_CHOOSER (widget),
(GtkFontFilterFunc)proxy_font_chooser_filter_monospace_cb, GINT_TO_POINTER (monospace), NULL);
#else
GtkComboBox* combo;
gint n_families, i;
PangoContext* context;
PangoFontFamily** families;
widget = gtk_combo_box_text_new ();
combo = GTK_COMBO_BOX (widget);
context = gtk_widget_get_pango_context (widget);
pango_context_list_families (context, &families, &n_families);
if (string) if (string)
{ {
gint j = 0; gint j = 0;
for (i = 0; i < n_families; i++) for (i = 0; i < n_families; i++)
{ {
const gchar* font = pango_font_family_get_name (families[i]);
if (monospace != pango_font_family_is_monospace (families[i])) if (monospace != pango_font_family_is_monospace (families[i]))
continue; continue;
const gchar* font = pango_font_family_get_name (families[i]);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), font); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), font);
if (!g_ascii_strcasecmp (font, string)) if (!g_ascii_strcasecmp (font, string))
gtk_combo_box_set_active (combo, j); gtk_combo_box_set_active (combo, j);
@ -564,22 +648,13 @@ katze_property_proxy (gpointer object,
g_signal_connect (widget, "changed", g_signal_connect (widget, "changed",
G_CALLBACK (proxy_combo_box_text_changed_cb), object); G_CALLBACK (proxy_combo_box_text_changed_cb), object);
g_free (families); g_free (families);
#endif
} }
else if (type == G_TYPE_PARAM_STRING && hint && g_str_has_prefix (hint, "application-")) else if (type == G_TYPE_PARAM_STRING && hint && g_str_has_prefix (hint, "application-"))
{ {
GtkListStore* model; GtkListStore* model;
GtkCellRenderer* renderer; GtkCellRenderer* renderer;
GtkComboBox* combo;
GList* apps;
const gchar* app_type = &hint[12]; const gchar* app_type = &hint[12];
GtkSettings* settings;
gint icon_width = 16;
GtkTreeIter iter_none;
GAppInfo* info;
settings = gtk_settings_get_for_screen (gdk_screen_get_default ());
gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
&icon_width, NULL);
model = gtk_list_store_new (4, G_TYPE_APP_INFO, G_TYPE_STRING, model = gtk_list_store_new (4, G_TYPE_APP_INFO, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_INT); G_TYPE_STRING, G_TYPE_INT);
@ -591,77 +666,10 @@ katze_property_proxy (gpointer object,
renderer = gtk_cell_renderer_text_new (); renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), renderer, "text", 2); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), renderer, "text", 2);
combo = GTK_COMBO_BOX (widget);
apps = g_app_info_get_all_for_type (app_type);
if (!apps)
apps = katze_app_info_get_all_for_category (app_type);
string = katze_object_get_string (object, property); g_object_set_data_full (G_OBJECT (widget), "app-type", g_strdup (app_type), g_free);
if (!g_strcmp0 (string, "")) g_object_set_data_full (G_OBJECT (widget), "object", g_object_ref (object), g_object_unref);
katze_assign (string, NULL); g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc)proxy_populate_apps, widget, NULL);
gtk_list_store_insert_with_values (model, &iter_none, 0,
0, NULL, 1, NULL, 2, _("None"), 3, icon_width, -1);
if (apps != NULL)
{
gint i = 0;
while ((info = g_list_nth_data (apps, i++)))
{
const gchar* name = g_app_info_get_name (info);
GIcon* icon = g_app_info_get_icon (info);
gchar* icon_name;
GtkTreeIter iter;
if (!g_app_info_should_show (info))
continue;
icon_name = icon ? g_icon_to_string (icon) : NULL;
gtk_list_store_insert_with_values (model, &iter, G_MAXINT,
0, info, 1, icon_name, 2, name, 3, icon_width, -1);
if (string && !strcmp (katze_app_info_get_commandline (info), string))
gtk_combo_box_set_active_iter (combo, &iter);
g_free (icon_name);
}
g_list_free (apps);
}
{
info = g_app_info_create_from_commandline ("",
"", G_APP_INFO_CREATE_NONE, NULL);
gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
0, info, 1, NULL, 2, _("Custom..."), 3, icon_width, -1);
g_object_unref (info);
if (gtk_combo_box_get_active (combo) == -1)
{
if (string)
{
GtkWidget* entry;
const gchar* exe;
info = g_app_info_create_from_commandline (string,
NULL, G_APP_INFO_CREATE_NONE, NULL);
entry = gtk_entry_new ();
exe = g_app_info_get_executable (info);
if (exe && *exe && strcmp (exe, "%f"))
gtk_entry_set_text (GTK_ENTRY (entry), string);
gtk_widget_show (entry);
gtk_container_add (GTK_CONTAINER (combo), entry);
g_object_unref (info);
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_entry_focus_out_event_cb), object);
g_object_set_data_full (G_OBJECT (entry), "property",
g_strdup (property), g_free);
}
else
gtk_combo_box_set_active_iter (combo, &iter_none);
}
}
g_signal_connect (widget, "changed",
G_CALLBACK (proxy_combo_box_apps_changed_cb), object);
} }
else if (type == G_TYPE_PARAM_STRING) else if (type == G_TYPE_PARAM_STRING)
{ {
@ -699,7 +707,8 @@ katze_property_proxy (gpointer object,
} }
else if (type == G_TYPE_PARAM_FLOAT) else if (type == G_TYPE_PARAM_FLOAT)
{ {
gfloat value = katze_object_get_float (object, property); gfloat value;
g_object_get (object, property, &value, NULL);
widget = gtk_spin_button_new_with_range ( widget = gtk_spin_button_new_with_range (
G_PARAM_SPEC_FLOAT (pspec)->minimum, G_PARAM_SPEC_FLOAT (pspec)->minimum,
@ -742,10 +751,7 @@ katze_property_proxy (gpointer object,
widget = gtk_spin_button_new_with_range ( widget = gtk_spin_button_new_with_range (
G_PARAM_SPEC_INT (pspec)->minimum, G_PARAM_SPEC_INT (pspec)->minimum,
G_PARAM_SPEC_INT (pspec)->maximum, 1); G_PARAM_SPEC_INT (pspec)->maximum, 1);
#if HAVE_HILDON
hildon_gtk_entry_set_input_mode (GTK_ENTRY (widget),
HILDON_GTK_INPUT_MODE_NUMERIC);
#endif
/* Keep it narrow, 5 digits are usually fine */ /* Keep it narrow, 5 digits are usually fine */
gtk_entry_set_width_chars (GTK_ENTRY (widget), 5); gtk_entry_set_width_chars (GTK_ENTRY (widget), 5);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
@ -763,39 +769,17 @@ katze_property_proxy (gpointer object,
if (hint && g_str_has_prefix (hint, "custom-")) if (hint && g_str_has_prefix (hint, "custom-"))
custom = &hint[7]; custom = &hint[7];
#ifdef HAVE_HILDON_2_2
GtkWidget* selector;
widget = hildon_picker_button_new (
HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
selector = hildon_touch_selector_new_text ();
hildon_button_set_title (HILDON_BUTTON (widget), gettext (nick));
hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (widget),
HILDON_TOUCH_SELECTOR (selector));
#else
widget = gtk_combo_box_text_new (); widget = gtk_combo_box_text_new ();
#endif
for (i = 0; i < enum_class->n_values; i++) for (i = 0; i < enum_class->n_values; i++)
{ {
const gchar* raw_label = gettext (enum_class->values[i].value_nick); const gchar* raw_label = gettext (enum_class->values[i].value_nick);
gchar* label = katze_strip_mnemonics (raw_label); gchar* label = katze_strip_mnemonics (raw_label);
#ifdef HAVE_HILDON_2_2
hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), label);
#else
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), label); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), label);
#endif
g_free (label); g_free (label);
} }
#ifdef HAVE_HILDON_2_2
hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (selector), 0, value);
g_signal_connect (widget, "value-changed",
G_CALLBACK (proxy_picker_button_changed_cb), object);
#else
gtk_combo_box_set_active (GTK_COMBO_BOX (widget), value); gtk_combo_box_set_active (GTK_COMBO_BOX (widget), value);
g_signal_connect (widget, "changed", g_signal_connect (widget, "changed",
G_CALLBACK (proxy_combo_box_changed_cb), object); G_CALLBACK (proxy_combo_box_changed_cb), object);
#endif
if (custom) if (custom)
{ {
gchar* custom_text = katze_object_get_string (object, custom); gchar* custom_text = katze_object_get_string (object, custom);
@ -812,11 +796,10 @@ katze_property_proxy (gpointer object,
G_CALLBACK (proxy_entry_focus_out_event_cb), object); G_CALLBACK (proxy_entry_focus_out_event_cb), object);
g_object_set_data_full (G_OBJECT (entry), "property", g_object_set_data_full (G_OBJECT (entry), "property",
g_strdup (custom), g_free); g_strdup (custom), g_free);
gtk_widget_set_tooltip_text (widget, NULL);
} }
#if GTK_CHECK_VERSION (2, 12, 0)
else else
gtk_widget_set_tooltip_text (widget, custom_text); gtk_widget_set_tooltip_text (widget, custom_text);
#endif
g_free (custom_text); g_free (custom_text);
@ -831,10 +814,6 @@ katze_property_proxy (gpointer object,
widget = gtk_label_new (gettext (nick)); widget = gtk_label_new (gettext (nick));
g_free (string); g_free (string);
#if GTK_CHECK_VERSION (2, 12, 0)
if (!gtk_widget_get_tooltip_text (widget))
gtk_widget_set_tooltip_text (widget, g_param_spec_get_blurb (pspec));
#endif
gtk_widget_set_sensitive (widget, pspec->flags & G_PARAM_WRITABLE); gtk_widget_set_sensitive (widget, pspec->flags & G_PARAM_WRITABLE);
g_object_set_data_full (G_OBJECT (widget), "property", g_object_set_data_full (G_OBJECT (widget), "property",
@ -843,53 +822,6 @@ katze_property_proxy (gpointer object,
return widget; return widget;
} }
/**
* katze_property_label:
* @object: a #GObject
* @property: the name of a property
*
* Create a label widget displaying the name of the specified object's property.
*
* Return value: a new label widget
*
* Since 0.2.1 the label will be empty if the property proxy for the
* same property would contain a label already.
**/
GtkWidget*
katze_property_label (gpointer object,
const gchar* property)
{
GObjectClass* class;
GParamSpec* pspec;
const gchar* nick;
GtkWidget* widget;
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
class = G_OBJECT_GET_CLASS (object);
pspec = g_object_class_find_property (class, property);
if (!pspec)
{
g_warning (_("Property '%s' is invalid for %s"),
property, G_OBJECT_CLASS_NAME (class));
return gtk_label_new (property);
}
#ifdef HAVE_HILDON_2_2
if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_ENUM)
return gtk_label_new (NULL);
#endif
nick = g_param_spec_get_nick (pspec);
widget = gtk_label_new (nick);
#if GTK_CHECK_VERSION (2, 12, 0)
gtk_widget_set_tooltip_text (widget, g_param_spec_get_blurb (pspec));
#endif
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
return widget;
}
typedef struct typedef struct
{ {
GtkWidget* widget; GtkWidget* widget;
@ -910,21 +842,29 @@ katze_widget_popup_position_menu (GtkMenu* menu,
GtkRequisition widget_req; GtkRequisition widget_req;
KatzePopupInfo* info = user_data; KatzePopupInfo* info = user_data;
GtkWidget* widget = info->widget; GtkWidget* widget = info->widget;
GdkWindow* window = gtk_widget_get_window (widget);
gint widget_height; gint widget_height;
gtk_widget_get_allocation (widget, &allocation); if (!window)
return;
#if !GTK_CHECK_VERSION (3, 0, 0)
if (GTK_IS_ENTRY (widget))
window = gdk_window_get_parent (window);
#endif
/* Retrieve size and position of both widget and menu */ /* Retrieve size and position of both widget and menu */
if (!gtk_widget_get_has_window (widget)) gtk_widget_get_allocation (widget, &allocation);
{ gdk_window_get_origin (window, &wx, &wy);
gdk_window_get_position (gtk_widget_get_window (widget), &wx, &wy);
wx += allocation.x; wx += allocation.x;
wy += allocation.y; wy += allocation.y;
} #if GTK_CHECK_VERSION (3, 0, 0)
else gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_req, NULL);
gdk_window_get_origin (gtk_widget_get_window (widget), &wx, &wy); gtk_widget_get_preferred_size (widget, &widget_req, NULL);
#else
gtk_widget_size_request (GTK_WIDGET (menu), &menu_req); gtk_widget_size_request (GTK_WIDGET (menu), &menu_req);
gtk_widget_size_request (widget, &widget_req); gtk_widget_size_request (widget, &widget_req);
#endif
menu_width = menu_req.width; menu_width = menu_req.width;
widget_height = widget_req.height; /* Better than allocation.height */ widget_height = widget_req.height; /* Better than allocation.height */
@ -1016,59 +956,6 @@ katze_image_menu_item_new_ellipsized (const gchar* label)
return menuitem; return menuitem;
} }
/**
* katze_pixbuf_new_from_buffer:
* @buffer: Buffer with image data
* @length: Length of the buffer
* @mime_type: a MIME type, or %NULL
* @error: return location for a #GError, or %NULL
*
* Creates a new #GdkPixbuf out of the specified buffer.
*
* You can specify a MIME type if looking at the buffer
* is not enough to determine the right type.
*
* Return value: A newly-allocated #GdkPixbuf
**/
GdkPixbuf*
katze_pixbuf_new_from_buffer (const guchar* buffer,
gsize length,
const gchar* mime_type,
GError** error)
{
/* Proposed for inclusion in GdkPixbuf
See http://bugzilla.gnome.org/show_bug.cgi?id=74291 */
GdkPixbufLoader* loader;
GdkPixbuf* pixbuf;
g_return_val_if_fail (buffer != NULL, NULL);
g_return_val_if_fail (length > 0, NULL);
if (mime_type)
{
loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error);
if (!loader)
return NULL;
}
else
loader = gdk_pixbuf_loader_new ();
if (!gdk_pixbuf_loader_write (loader, buffer, length, error))
{
g_object_unref (loader);
return NULL;
}
if (!gdk_pixbuf_loader_close (loader, error))
{
g_object_unref (loader);
return NULL;
}
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
g_object_ref (pixbuf);
g_object_unref (loader);
return pixbuf;
}
/** /**
* katze_tree_view_get_selected_iter: * katze_tree_view_get_selected_iter:
* @treeview: a #GtkTreeView * @treeview: a #GtkTreeView
@ -1190,27 +1077,14 @@ katze_strip_mnemonics (const gchar* original)
return result; return result;
} }
/** const gchar*
* katze_object_has_property: katze_skip_whitespace (const gchar* str)
* @object: a #GObject
* @property: the name of the property
*
* Determine if @object has a property with the specified name.
*
* Return value: a boolean
*
* Since: 0.1.2
**/
gboolean
katze_object_has_property (gpointer object,
const gchar* property)
{ {
GObjectClass* class; if (str == NULL)
return NULL;
g_return_val_if_fail (G_IS_OBJECT (object), FALSE); while (*str == ' ' || *str == '\t' || *str == '\n')
str++;
class = G_OBJECT_GET_CLASS (object); return str;
return g_object_class_find_property (class, property) != NULL;
} }
/** /**
@ -1257,28 +1131,6 @@ katze_object_get_int (gpointer object,
return value; return value;
} }
/**
* katze_object_get_float:
* @object: a #GObject
* @property: the name of the property to get
*
* Retrieve the float value of the specified property.
*
* Return value: a float
**/
gfloat
katze_object_get_float (gpointer object,
const gchar* property)
{
gfloat value = -1.0f;
g_return_val_if_fail (G_IS_OBJECT (object), -1.0f);
/* FIXME: Check value type */
g_object_get (object, property, &value, NULL);
return value;
}
/** /**
* katze_object_get_enum: * katze_object_get_enum:
* @object: a #GObject * @object: a #GObject
@ -1353,165 +1205,17 @@ katze_object_get_object (gpointer object,
* Create a directory if it doesn't already exist. Create intermediate * Create a directory if it doesn't already exist. Create intermediate
* parent directories as needed, too. * parent directories as needed, too.
* *
* Similar to g_mkdir_with_parents() but returning early if the
* @pathname refers to an existing directory.
*
* Returns: 0 if the directory already exists, or was successfully * Returns: 0 if the directory already exists, or was successfully
* created. Returns -1 if an error occurred, with errno set. * created. Returns -1 if an error occurred, with errno set.
* *
* Since: 0.2.1 * Since: 0.2.1
*/ */
/* Creating directories recursively
Copyright 2000 Red Hat, Inc.
Originally copied from Glib 2.20, coding style adjusted
Modified to determine file existence early and pathname must be != NULL */
int int
katze_mkdir_with_parents (const gchar* pathname, katze_mkdir_with_parents (const gchar* pathname,
int mode) int mode)
{ {
gchar* fn, *p; midori_paths_mkdir_with_parents (pathname, mode);
/* Use g_access instead of g_file_test for better performance */
if (g_access (pathname, F_OK) == 0)
return 0; return 0;
fn = g_strdup (pathname);
if (g_path_is_absolute (fn))
p = (gchar *) g_path_skip_root (fn);
else
p = fn;
do
{
while (*p && !G_IS_DIR_SEPARATOR (*p))
p++;
if (!*p)
p = NULL;
else
*p = '\0';
if (g_access (fn, F_OK) != 0)
{
if (g_mkdir (fn, mode) == -1)
{
g_free (fn);
return -1;
}
}
else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
{
g_free (fn);
return -1;
}
if (p)
{
*p++ = G_DIR_SEPARATOR;
while (*p && G_IS_DIR_SEPARATOR (*p))
p++;
}
}
while (p);
g_free (fn);
return 0;
}
/**
* katze_widget_has_touchscreen_mode:
* @widget: a #GtkWidget, or %NULL
*
* Determines whether @widget should operate in touchscreen
* mode, as determined by GtkSettings or the environment
* variable MIDORI_TOUCHSCREEN.
*
* If @widget is %NULL, the default screen will be used.
*
* Returns: %TRUE if touchscreen mode should be used
*
* Since: 0.2.1
*/
gboolean
katze_widget_has_touchscreen_mode (GtkWidget* widget)
{
const gchar* touchscreen = g_getenv ("MIDORI_TOUCHSCREEN");
if (touchscreen && touchscreen[0] == '1')
return TRUE;
else if (touchscreen && touchscreen[0] == '0')
return FALSE;
else
{
GdkScreen* screen = widget && gtk_widget_has_screen (widget)
? gtk_widget_get_screen (widget) : gdk_screen_get_default ();
GtkSettings* gtk_settings = gtk_settings_get_for_screen (screen);
gboolean enabled;
g_object_get (gtk_settings, "gtk-touchscreen-mode", &enabled, NULL);
return enabled;
}
}
/**
* katze_load_cached_icon:
* @uri: an URI string
* @widget: a #GtkWidget, or %NULL
*
* Loads a cached icon for the specified @uri. If there is no
* icon and @widget is specified, a default will be returned.
*
* Returns: a #GdkPixbuf, or %NULL
*
* Since: 0.2.2
*/
GdkPixbuf*
katze_load_cached_icon (const gchar* uri,
GtkWidget* widget)
{
GdkPixbuf* icon = NULL;
g_return_val_if_fail (uri != NULL, NULL);
if (midori_uri_is_http (uri))
{
guint i;
gchar* icon_uri;
gchar* checksum;
gchar* ext;
gchar* filename;
gchar* path;
i = 8;
while (uri[i] != '\0' && uri[i] != '/')
i++;
if (uri[i] == '/')
{
gchar* ticon_uri = g_strdup (uri);
ticon_uri[i] = '\0';
icon_uri = g_strdup_printf ("%s/favicon.ico", ticon_uri);
g_free (ticon_uri);
}
else
icon_uri = g_strdup_printf ("%s/favicon.ico", uri);
checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, icon_uri, -1);
ext = g_strrstr (icon_uri, ".");
filename = g_strdup_printf ("%s%s", checksum, ext ? ext : "");
g_free (icon_uri);
g_free (checksum);
path = g_build_filename (g_get_user_cache_dir (), PACKAGE_NAME,
"icons", filename, NULL);
g_free (filename);
if ((icon = gdk_pixbuf_new_from_file_at_size (path, 16, 16, NULL)))
{
g_free (path);
return icon;
}
g_free (path);
}
return icon || !widget ? icon : gtk_widget_render_icon (widget,
GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
} }
static void static void
@ -1520,7 +1224,16 @@ katze_uri_entry_changed_cb (GtkWidget* entry,
{ {
const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry)); const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry));
gboolean valid = midori_uri_is_location (uri); gboolean valid = midori_uri_is_location (uri);
if (*uri && !valid) if (!valid && g_object_get_data (G_OBJECT (entry), "allow_%s"))
valid = uri && g_str_has_prefix (uri, "%s");
if (!valid)
valid = midori_uri_is_ip_address (uri);
#if GTK_CHECK_VERSION (3, 2, 0)
g_object_set_data (G_OBJECT (entry), "invalid", GINT_TO_POINTER (uri && *uri && !valid));
gtk_widget_queue_draw (entry);
#else
if (uri && *uri && !valid)
{ {
GdkColor bg_color = { 0 }; GdkColor bg_color = { 0 };
GdkColor fg_color = { 0 }; GdkColor fg_color = { 0 };
@ -1534,11 +1247,34 @@ katze_uri_entry_changed_cb (GtkWidget* entry,
gtk_widget_modify_base (entry, GTK_STATE_NORMAL, NULL); gtk_widget_modify_base (entry, GTK_STATE_NORMAL, NULL);
gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL); gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL);
} }
#endif
if (other_widget != NULL) if (other_widget != NULL)
gtk_widget_set_sensitive (other_widget, valid); gtk_widget_set_sensitive (other_widget, valid);
} }
#if GTK_CHECK_VERSION (3, 2, 0)
static gboolean
katze_uri_entry_draw_cb (GtkWidget* entry,
cairo_t* cr,
GtkWidget* other_widget)
{
const GdkRGBA color = { 0.9, 0., 0., 1. };
double width = gtk_widget_get_allocated_width (entry);
double height = gtk_widget_get_allocated_height (entry);
if (!g_object_get_data (G_OBJECT (entry), "invalid"))
return FALSE;
/* FIXME: error-underline-color requires GtkTextView */
gdk_cairo_set_source_rgba (cr, &color);
pango_cairo_show_error_underline (cr, width * 0.15, height / 1.9,
width * 0.75, height / 1.9 / 2);
return TRUE;
}
#endif
/** /**
* katze_uri_entry_new: * katze_uri_entry_new:
* @other_widget: a #GtkWidget, or %NULL * @other_widget: a #GtkWidget, or %NULL
@ -1556,11 +1292,31 @@ GtkWidget*
katze_uri_entry_new (GtkWidget* other_widget) katze_uri_entry_new (GtkWidget* other_widget)
{ {
GtkWidget* entry = gtk_entry_new (); GtkWidget* entry = gtk_entry_new ();
#if GTK_CHECK_VERSION (3, 6, 0)
gtk_entry_set_input_purpose (GTK_ENTRY (entry), GTK_INPUT_PURPOSE_URL);
#endif
gtk_entry_set_icon_from_gicon (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY,
g_themed_icon_new_with_default_fallbacks ("text-html-symbolic"));
g_signal_connect (entry, "changed", g_signal_connect (entry, "changed",
G_CALLBACK (katze_uri_entry_changed_cb), other_widget); G_CALLBACK (katze_uri_entry_changed_cb), other_widget);
#if GTK_CHECK_VERSION (3, 2, 0)
g_signal_connect_after (entry, "draw",
G_CALLBACK (katze_uri_entry_draw_cb), other_widget);
#endif
return entry; return entry;
} }
void
katze_widget_add_class (GtkWidget* widget,
const gchar* class_name)
{
#if GTK_CHECK_VERSION (3,0,0)
GtkStyleContext* context = gtk_widget_get_style_context (widget);
gtk_style_context_add_class (context, class_name);
#endif
}
/** /**
* katze_assert_str_equal: * katze_assert_str_equal:
* @input: a string * @input: a string
@ -1585,3 +1341,21 @@ katze_assert_str_equal (const gchar* input,
} }
} }
void
katze_window_set_sensible_default_size (GtkWindow* window)
{
GdkScreen* screen;
GdkRectangle monitor;
gint width, height;
g_return_if_fail (GTK_IS_WINDOW (window));
screen = gtk_window_get_screen (window);
gdk_screen_get_monitor_geometry (screen, 0, &monitor);
width = monitor.width / 1.7;
height = monitor.height / 1.7;
gtk_window_set_default_size (window, width, height);
/* 700x100 is the approximate useful minimum dimensions */
gtk_widget_set_size_request (GTK_WIDGET (window), 700, 100);
}

View file

@ -19,15 +19,6 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/**
* KATZE_OBJECT_NAME:
* @object: an object
*
* Return the name of an object's class structure's type.
**/
#define KATZE_OBJECT_NAME(object) \
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object))
/** /**
* katze_assign: * katze_assign:
* @lvalue: a pointer * @lvalue: a pointer
@ -45,7 +36,7 @@ G_BEGIN_DECLS
* Unrefs @lvalue if needed and assigns it the value of @rvalue. * Unrefs @lvalue if needed and assigns it the value of @rvalue.
**/ **/
#define katze_object_assign(lvalue, rvalue) \ #define katze_object_assign(lvalue, rvalue) \
lvalue = ((lvalue ? g_object_unref (lvalue) : lvalue), rvalue) lvalue = ((lvalue ? g_object_unref (lvalue) : (void)0), rvalue)
/** /**
* katze_strv_assign: * katze_strv_assign:
@ -58,15 +49,25 @@ G_BEGIN_DECLS
**/ **/
#define katze_strv_assign(lvalue, rvalue) lvalue = (g_strfreev (lvalue), rvalue) #define katze_strv_assign(lvalue, rvalue) lvalue = (g_strfreev (lvalue), rvalue)
/**
* katze_str_non_null:
* @str: a string, or %NULL
*
* Returns "" if @str is %NULL.
*
* Since: 0.4.4
**/
static inline const gchar*
katze_str_non_null (const gchar* str)
{
return str ? str : "";
}
GtkWidget* GtkWidget*
katze_property_proxy (gpointer object, katze_property_proxy (gpointer object,
const gchar* property, const gchar* property,
const gchar* hint); const gchar* hint);
GtkWidget*
katze_property_label (gpointer object,
const gchar* property);
typedef enum { typedef enum {
KATZE_MENU_POSITION_CURSOR = 0, KATZE_MENU_POSITION_CURSOR = 0,
KATZE_MENU_POSITION_LEFT, KATZE_MENU_POSITION_LEFT,
@ -82,12 +83,6 @@ katze_widget_popup (GtkWidget* widget,
GtkWidget* GtkWidget*
katze_image_menu_item_new_ellipsized (const gchar* label); katze_image_menu_item_new_ellipsized (const gchar* label);
GdkPixbuf*
katze_pixbuf_new_from_buffer (const guchar* buffer,
gsize length,
const gchar* mime_type,
GError** error);
gboolean gboolean
katze_tree_view_get_selected_iter (GtkTreeView* treeview, katze_tree_view_get_selected_iter (GtkTreeView* treeview,
GtkTreeModel** model, GtkTreeModel** model,
@ -101,9 +96,8 @@ katze_bookmark_populate_tree_view (KatzeArray* array,
gchar* gchar*
katze_strip_mnemonics (const gchar* original); katze_strip_mnemonics (const gchar* original);
gboolean const gchar*
katze_object_has_property (gpointer object, katze_skip_whitespace (const gchar* str);
const gchar* property);
gint gint
katze_object_get_boolean (gpointer object, katze_object_get_boolean (gpointer object,
@ -113,10 +107,6 @@ gint
katze_object_get_int (gpointer object, katze_object_get_int (gpointer object,
const gchar* property); const gchar* property);
gfloat
katze_object_get_float (gpointer object,
const gchar* property);
gint gint
katze_object_get_enum (gpointer object, katze_object_get_enum (gpointer object,
const gchar* property); const gchar* property);
@ -133,21 +123,21 @@ int
katze_mkdir_with_parents (const gchar* pathname, katze_mkdir_with_parents (const gchar* pathname,
int mode); int mode);
gboolean
katze_widget_has_touchscreen_mode (GtkWidget* widget);
GdkPixbuf*
katze_load_cached_icon (const gchar* uri,
GtkWidget* widget);
GtkWidget* GtkWidget*
katze_uri_entry_new (GtkWidget* other_widget); katze_uri_entry_new (GtkWidget* other_widget);
void
katze_widget_add_class (GtkWidget* widget,
const gchar* class_name);
void void
katze_assert_str_equal (const gchar* input, katze_assert_str_equal (const gchar* input,
const gchar* result, const gchar* result,
const gchar* expected); const gchar* expected);
void
katze_window_set_sensible_default_size (GtkWindow* window);
G_END_DECLS G_END_DECLS
#endif /* __KATZE_UTILS_H__ */ #endif /* __KATZE_UTILS_H__ */

View file

@ -20,9 +20,7 @@
#include "katze-item.h" #include "katze-item.h"
#include "katze-array.h" #include "katze-array.h"
#include "katze-arrayaction.h" #include "katze-arrayaction.h"
#include "katze-separatoraction.h"
#include "katze-net.h" #include "katze-net.h"
#include "katze-scrolled.h"
#include "katze-preferences.h" #include "katze-preferences.h"
#endif /* __KATZE_H__ */ #endif /* __KATZE_H__ */

38
katze/katze.vapi Normal file
View file

@ -0,0 +1,38 @@
/* Copyright (C) 2012 André Stösel <andre@stoesel.de>
This file is licensed under the terms of the expat license, see the file EXPAT. */
[CCode (cprefix = "Katze", lower_case_cprefix = "katze_")]
namespace Katze {
static void assert_str_equal (string input, string result, string? expected);
static unowned Gtk.Widget property_proxy (void* object, string property, string? hint);
[CCode (cheader_filename = "katze/katze.h", cprefix = "KATZE_MENU_POSITION_")]
enum MenuPos {
CURSOR,
LEFT,
RIGHT
}
static void widget_popup (Gtk.Widget? widget, Gtk.Menu menu, Gdk.EventButton? event, MenuPos pos);
[CCode (cheader_filename = "katze/katze.h")]
public class Array : Katze.Item {
public Array (GLib.Type type);
public signal void add_item (GLib.Object item);
public signal void remove_item (GLib.Object item);
public uint get_length ();
public GLib.List<unowned Item> get_items ();
public bool is_empty ();
}
[CCode (cheader_filename = "katze/katze.h")]
public class Item : GLib.Object {
public Item ();
public string? uri { get; set; }
public string? name { get; set; }
public string? text { get; set; }
public bool get_meta_boolean (string key);
public int64 get_meta_integer (string key);
public void set_meta_integer (string key, int64 value);
}
}

145
katze/midori-hsts.vala Normal file
View file

@ -0,0 +1,145 @@
/*
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
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 class HSTS : GLib.Object, Soup.SessionFeature {
public class Directive {
public Soup.Date? expires = null;
public bool sub_domains = false;
public Directive (bool include_sub_domains) {
expires = new Soup.Date.from_now (int.MAX);
sub_domains = include_sub_domains;
}
public Directive.from_header (string header) {
var param_list = Soup.header_parse_param_list (header);
if (param_list == null)
return;
string? max_age = param_list.lookup ("max-age");
if (max_age != null)
expires = new Soup.Date.from_now (max_age.to_int ());
// if (param_list.lookup_extended ("includeSubDomains", null, null))
if ("includeSubDomains" in header)
sub_domains = true;
Soup.header_free_param_list (param_list);
}
public bool is_valid () {
return expires != null && !expires.is_past ();
}
}
HashTable<string, Directive> whitelist;
bool debug = false;
public HSTS () {
whitelist = new HashTable<string, Directive> (str_hash, str_equal);
read_cache.begin (File.new_for_path (Paths.get_preset_filename (null, "hsts")));
read_cache.begin (File.new_for_path (Paths.get_config_filename_for_reading ("hsts")));
if (strcmp (Environment.get_variable ("MIDORI_DEBUG"), "hsts") == 0)
debug = true;
}
async void read_cache (File file) {
try {
var stream = new DataInputStream (yield file.read_async ());
do {
string? line = yield stream.read_line_async ();
if (line == null)
break;
string[] parts = line.split (" ", 2);
if (parts[0] == null || parts[1] == null)
break;
var directive = new Directive.from_header (parts[1]);
if (directive.is_valid ())
append_to_whitelist (parts[0], directive);
} while (true);
}
catch (Error error) { }
}
#if HAVE_LIBSOUP_2_34_0
/* No sub-features */
public bool add_feature (Type type) { return false; }
public bool remove_feature (Type type) { return false; }
public bool has_feature (Type type) { return false; }
#endif
public void attach (Soup.Session session) { session.request_queued.connect (queued); }
public void detach (Soup.Session session) { /* FIXME disconnect */ }
/* Never called but required by the interface */
public void request_started (Soup.Session session, Soup.Message msg, Soup.Socket socket) { }
public void request_queued (Soup.Session session, Soup.Message message) { }
public void request_unqueued (Soup.Session session, Soup.Message msg) { }
bool should_secure_host (string host) {
Directive? directive = whitelist.lookup (host);
if (directive == null)
directive = whitelist.lookup ("*." + host);
return directive != null && directive.is_valid ();
}
void queued (Soup.Session session, Soup.Message message) {
if (should_secure_host (message.uri.host)) {
message.uri.set_scheme ("https");
session.requeue_message (message);
if (debug)
stdout.printf ("HSTS: Enforce %s\n", message.uri.host);
}
else if (message.uri.scheme == "http")
message.finished.connect (strict_transport_security_handled);
}
void append_to_whitelist (string host, Directive directive) {
whitelist.insert (host, directive);
if (directive.sub_domains)
whitelist.insert ("*." + host, directive);
}
async void append_to_cache (string host, string header) {
if (Midori.Paths.is_readonly ())
return;
string filename = Paths.get_config_filename_for_writing ("hsts");
try {
var file = File.new_for_path (filename);
var stream = file.append_to/* FIXME _async*/ (FileCreateFlags.NONE);
yield stream.write_async ((host + " " + header + "\n").data);
yield stream.flush_async ();
}
catch (Error error) {
critical ("Failed to update %s: %s", filename, error.message);
}
}
void strict_transport_security_handled (Soup.Message message) {
if (message == null || message.uri == null)
return;
unowned string? hsts = message.response_headers.get_one ("Strict-Transport-Security");
if (hsts == null)
return;
var directive = new Directive.from_header (hsts);
if (directive.is_valid ()) {
append_to_whitelist (message.uri.host, directive);
append_to_cache.begin (message.uri.host, hsts);
}
if (debug)
stdout.printf ("HSTS: '%s' sets '%s' valid? %s\n",
message.uri.host, hsts, directive.is_valid ().to_string ());
}
}
}

468
katze/midori-paths.vala Normal file
View file

@ -0,0 +1,468 @@
/*
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
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 GLib {
#if HAVE_WIN32
extern static string win32_get_package_installation_directory_of_module (void* hmodule = null);
#endif
}
extern const string LIBDIR;
extern const string MDATADIR;
extern const string PACKAGE_NAME;
extern const string SYSCONFDIR;
extern const string MIDORI_VERSION_SUFFIX;
const string MODULE_PREFIX = "lib";
const string MODULE_SUFFIX = "." + GLib.Module.SUFFIX;
namespace Midori {
public enum RuntimeMode {
UNDEFINED,
NORMAL,
APP,
PRIVATE,
PORTABLE
}
namespace Paths {
static string? exec_path = null;
static string[] command_line = null;
static string? runtime_dir = null;
static RuntimeMode mode = RuntimeMode.UNDEFINED;
static string? config_dir = null;
static string? readonly_dir = null;
static string? cache_dir = null;
static string? cache_dir_for_reading = null;
static string? user_data_dir = null;
static string? user_data_dir_for_reading = null;
static string? tmp_dir = null;
namespace Test {
public void reset_runtime_mode () {
mode = RuntimeMode.UNDEFINED;
}
}
public static string get_config_dir_for_reading () {
assert (mode != RuntimeMode.UNDEFINED);
return readonly_dir ?? config_dir;
}
/* returns the path to a user configuration file whose contents should not be modified.
to get the path to save settings, use get_config_filename() */
public static string get_config_filename_for_reading (string filename) {
assert (mode != RuntimeMode.UNDEFINED);
return Path.build_path (Path.DIR_SEPARATOR_S,
readonly_dir ?? config_dir, filename);
}
public bool is_readonly () {
assert (mode != RuntimeMode.UNDEFINED);
return readonly_dir != null;
}
public RuntimeMode get_runtime_mode () {
assert (mode != RuntimeMode.UNDEFINED);
return mode;
}
public static unowned string get_runtime_dir () {
if (runtime_dir != null)
return runtime_dir;
#if HAVE_WIN32
runtime_dir = Environment.get_variable ("XDG_RUNTIME_DIR");
if (runtime_dir == null || runtime_dir == "")
runtime_dir = Environment.get_user_data_dir ();
#else
runtime_dir = Environment.get_variable ("XDG_RUNTIME_DIR");
if (runtime_dir == null || runtime_dir == "") {
runtime_dir = Path.build_path (Path.DIR_SEPARATOR_S,
Environment.get_tmp_dir (), PACKAGE_NAME + "-" + Environment.get_user_name ());
mkdir_with_parents (runtime_dir);
return runtime_dir;
}
#endif
runtime_dir = Path.build_path (Path.DIR_SEPARATOR_S, runtime_dir, PACKAGE_NAME);
mkdir_with_parents (runtime_dir);
return runtime_dir;
}
public static void init (RuntimeMode new_mode, string? config) {
assert (mode == RuntimeMode.UNDEFINED);
assert (new_mode != RuntimeMode.UNDEFINED);
mode = new_mode;
if (mode == RuntimeMode.PORTABLE || mode == RuntimeMode.PRIVATE)
Gtk.Settings.get_default ().gtk_recent_files_max_age = 0;
if (mode == RuntimeMode.PORTABLE) {
config_dir = Path.build_path (Path.DIR_SEPARATOR_S,
exec_path, "profile", "config");
cache_dir = Path.build_path (Path.DIR_SEPARATOR_S,
exec_path, "profile", "cache");
user_data_dir = Path.build_path (Path.DIR_SEPARATOR_S,
exec_path, "profile", "misc");
tmp_dir = Path.build_path (Path.DIR_SEPARATOR_S,
exec_path, "profile", "tmp");
}
else if (mode == RuntimeMode.PRIVATE || mode == RuntimeMode.APP) {
string? real_config = config != null && !Path.is_absolute (config)
? Path.build_filename (Environment.get_current_dir (), config) : config;
readonly_dir = real_config ?? Path.build_path (Path.DIR_SEPARATOR_S,
Environment.get_user_config_dir (), PACKAGE_NAME);
cache_dir_for_reading = Path.build_path (Path.DIR_SEPARATOR_S,
Environment.get_user_cache_dir (), PACKAGE_NAME);
user_data_dir_for_reading = Environment.get_user_data_dir ();
tmp_dir = get_runtime_dir ();
}
else {
string? real_config = config != null && !Path.is_absolute (config)
? Path.build_filename (Environment.get_current_dir (), config) : config;
config_dir = real_config ?? Path.build_path (Path.DIR_SEPARATOR_S,
Environment.get_user_config_dir (), PACKAGE_NAME);
cache_dir = Path.build_path (Path.DIR_SEPARATOR_S,
Environment.get_user_cache_dir (), PACKAGE_NAME);
user_data_dir = Environment.get_user_data_dir ();
#if HAVE_WEBKIT2_A
WebKit.WebContext.get_default ().set_disk_cache_directory (
Path.build_path (Path.DIR_SEPARATOR_S, cache_dir, "web"));
#endif
#if HAVE_WEBKIT2
var cookie_manager = WebKit.WebContext.get_default ().get_cookie_manager ();
cookie_manager.set_persistent_storage (Path.build_filename (config, "cookies.db"),
WebKit.CookiePersistentStorage.SQLITE);
#endif
tmp_dir = get_runtime_dir ();
}
#if HAVE_WEBKIT_1_3_13
if (user_data_dir != null) {
string folder = Path.build_filename (user_data_dir, "webkit", "icondatabase");
#if HAVE_WEBKIT2
WebKit.WebContext.get_default ().set_favicon_database_directory (folder);
#elif HAVE_WEBKIT_1_8_0
WebKit.get_favicon_database ().set_path (folder);
#elif HAVE_WEBKIT_1_3_13
WebKit.get_icon_database ().set_path (folder);
#endif
}
#endif
if (strcmp (Environment.get_variable ("MIDORI_DEBUG"), "paths") == 0) {
stdout.printf ("config: %s\ncache: %s\nuser_data: %s\ntmp: %s\n",
config_dir, cache_dir, user_data_dir, tmp_dir);
}
}
public static void mkdir_with_parents (string path, int mode = 0700) {
/* Use g_access instead of g_file_test for better performance */
if (Posix.access (path, Posix.F_OK) == 0)
return;
int i = path.index_of_char (Path.DIR_SEPARATOR, 0);
do {
string fn = path.substring (i, -1);
if (Posix.access (fn, Posix.F_OK) != 0) {
if (DirUtils.create (fn, mode) == -1) {
/* Slow fallback; if this fails we fail */
DirUtils.create_with_parents (path, mode);
return;
}
}
else if (!FileUtils.test (fn, FileTest.IS_SYMLINK))
return; /* Failed */
i = path.index_of_char (Path.DIR_SEPARATOR, i);
}
while (i != -1);
}
public static void remove_path (string path) {
try {
var dir = Dir.open (path, 0);
string? name;
while (true) {
name = dir.read_name ();
if (name == null)
break;
remove_path (Path.build_filename (path, name));
}
}
catch (Error error) {
FileUtils.remove (path);
}
}
public static unowned string get_config_dir_for_writing () {
assert (config_dir != null);
mkdir_with_parents (config_dir);
return config_dir;
}
public static string get_extension_config_dir (string extension) {
assert (config_dir != null);
string folder;
if ("." in extension)
folder = Path.build_filename (config_dir, "extensions", extension);
else
folder = Path.build_filename (config_dir, "extensions",
MODULE_PREFIX + extension + "." + GLib.Module.SUFFIX);
mkdir_with_parents (folder);
return folder;
}
public static string get_extension_preset_filename (string extension, string filename) {
assert (exec_path != null);
string preset_filename = extension;
if (extension.has_prefix (MODULE_PREFIX))
preset_filename = extension.split (MODULE_PREFIX)[1];
if (extension.has_suffix (MODULE_SUFFIX))
preset_filename = preset_filename.split (MODULE_SUFFIX)[0];
return get_preset_filename (Path.build_filename ("extensions", preset_filename), filename);
}
/* returns the path to a user configuration file to which it is permitted to write.
this is also necessary for files whose state is synchronized to disk by a manager,
e.g. cookies. */
public static string get_config_filename_for_writing (string filename) {
assert (mode != RuntimeMode.UNDEFINED);
assert (config_dir != null);
mkdir_with_parents (config_dir);
return Path.build_path (Path.DIR_SEPARATOR_S, config_dir, filename);
}
public static unowned string get_cache_dir () {
assert (cache_dir != null);
return cache_dir;
}
public static unowned string get_user_data_dir () {
assert (user_data_dir != null);
return user_data_dir;
}
public static unowned string get_user_data_dir_for_reading () {
assert (user_data_dir_for_reading != null || user_data_dir != null);
if (user_data_dir != null)
return user_data_dir;
return user_data_dir_for_reading;
}
public static unowned string get_cache_dir_for_reading () {
assert (cache_dir_for_reading != null || cache_dir != null);
if (cache_dir != null)
return cache_dir;
return cache_dir_for_reading;
}
public static unowned string get_tmp_dir () {
assert (tmp_dir != null);
return tmp_dir;
}
public static string make_tmp_dir (string tmpl) {
assert (tmp_dir != null);
#if HAVE_GLIB_2_30
try {
return DirUtils.make_tmp (tmpl);
}
catch (Error error) {
GLib.error (error.message);
}
#else
string folder = Path.build_path (Path.DIR_SEPARATOR_S, Environment.get_tmp_dir (), tmpl);
DirUtils.mkdtemp (folder);
return folder;
#endif
}
public static void init_exec_path (string[] new_command_line) {
assert (command_line == null);
command_line = new_command_line;
#if HAVE_WIN32
exec_path = Environment.get_variable ("MIDORI_EXEC_PATH") ??
win32_get_package_installation_directory_of_module ();
#else
string? executable;
try {
if (!Path.is_absolute (command_line[0])) {
string program = Environment.find_program_in_path (command_line[0]);
if (FileUtils.test (program, FileTest.IS_SYMLINK))
executable = FileUtils.read_link (program);
else
executable = program;
}
else
executable = FileUtils.read_link (command_line[0]);
}
catch (Error error) {
executable = command_line[0];
}
exec_path = File.new_for_path (executable).get_parent ().get_parent ().get_path ();
#endif
if (strcmp (Environment.get_variable ("MIDORI_DEBUG"), "paths") == 0) {
stdout.printf ("command_line: %s\nexec_path: %s\nres: %s\nlib: %s\n",
get_command_line_str (true), exec_path,
get_res_filename (""), get_lib_path (PACKAGE_NAME));
}
}
public static unowned string[] get_command_line () {
assert (command_line != null);
return command_line;
}
public static string get_command_line_str (bool for_display) {
assert (command_line != null);
if (for_display)
return string.joinv (" ", command_line).replace (Environment.get_home_dir (), "~");
return string.joinv (" ", command_line).replace ("--debug", "").replace ("-g", "")
.replace ("--diagnostic-dialog", "").replace ("-d", "");
}
public static string get_lib_path (string package) {
assert (command_line != null);
#if HAVE_WIN32
return Path.build_filename (exec_path, "lib", package);
#else
string path = Path.build_filename (exec_path, "lib", package);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
if (package == PACKAGE_NAME) {
/* Fallback to build folder */
path = Path.build_filename ((File.new_for_path (exec_path).get_path ()), "extensions");
if (Posix.access (path, Posix.F_OK) == 0)
return path;
}
return Path.build_filename (LIBDIR, PACKAGE_NAME);
#endif
}
public static string get_res_filename (string filename) {
assert (command_line != null);
#if HAVE_WIN32
return Path.build_filename (exec_path, "share", PACKAGE_NAME, "res", filename);
#else
string path = Path.build_filename (exec_path, "share", PACKAGE_NAME, "res", filename);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
/* Fallback to build folder */
path = Path.build_filename ((File.new_for_path (exec_path)
.get_parent ().get_parent ().get_path ()), "data", filename);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
return Path.build_filename (MDATADIR, PACKAGE_NAME, "res", filename);
#endif
}
/* returns the path to a file containing read-only data installed with the application
if @res is true, looks in the midori resource folder specifically */
public static string get_data_filename (string filename, bool res) {
assert (command_line != null);
string res1 = res ? PACKAGE_NAME : "";
string res2 = res ? "res" : "";
#if HAVE_WIN32
return Path.build_filename (exec_path, "share", res1, res2, filename);
#else
string path = Path.build_filename (get_user_data_dir_for_reading (), res1, res2, filename);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
foreach (string data_dir in Environment.get_system_data_dirs ()) {
path = Path.build_filename (data_dir, res1, res2, filename);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
}
return Path.build_filename (MDATADIR, res1, res2, filename);
#endif
}
/* returns the path to a file containing system default configuration */
public static string get_preset_filename (string? folder, string filename) {
assert (exec_path != null);
#if HAVE_WIN32
return Path.build_filename (exec_path, "etc", "xdg", PACKAGE_NAME, folder ?? "", filename);
#else
foreach (string config_dir in Environment.get_system_config_dirs ()) {
string path = Path.build_filename (config_dir, PACKAGE_NAME, folder ?? "", filename);
if (Posix.access (path, Posix.F_OK) == 0)
return path;
}
return Path.build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, folder ?? "", filename);
#endif
}
public static void clear_icons () {
assert (cache_dir != null);
assert (user_data_dir != null);
#if HAVE_WEBKIT2
WebKit.WebContext.get_default ().get_favicon_database ().clear ();
#elif HAVE_WEBKIT_1_8_0
WebKit.get_favicon_database ().clear ();
#elif HAVE_WEBKIT_1_3_13
WebKit.get_icon_database ().clear ();
#endif
/* FIXME: Exclude search engine icons */
remove_path (Path.build_filename (cache_dir, "icons"));
remove_path (Path.build_filename (user_data_dir, "webkit", "icondatabase"));
}
public static Gdk.Pixbuf? get_icon (string? uri, Gtk.Widget? widget) {
if (!Midori.URI.is_resource (uri))
return null;
int icon_width = 16, icon_height = 16;
if (widget != null)
Gtk.icon_size_lookup_for_settings (widget.get_settings (),
Gtk.IconSize.MENU, out icon_width, out icon_height);
#if HAVE_WEBKIT2
/* TODO async
var database = WebKit.WebContext.get_default ().get_favicon_database ();
database.get_favicon.begin (uri, null); */
#elif HAVE_WEBKIT_1_8_0
Gdk.Pixbuf? pixbuf = WebKit.get_favicon_database ()
.try_get_favicon_pixbuf (uri, icon_width, icon_height);
if (pixbuf != null)
return pixbuf;
#elif HAVE_WEBKIT_1_3_13
Gdk.Pixbuf? pixbuf = WebKit.get_icon_database ().get_icon_pixbuf (uri);
if (pixbuf != null)
return pixbuf.scale_simple (icon_width, icon_height, Gdk.InterpType.BILINEAR);
#else
if (Midori.URI.is_http (uri)) {
try {
uint i = 8;
while (uri[i] != '\0' && uri[i] != '/')
i++;
string icon_uri = (uri[i] == '/')
? uri.substring (0, i) + "/favicon.ico"
: uri + "/favicon.ico";
string checksum = Checksum.compute_for_string (ChecksumType.MD5, icon_uri, -1);
string filename = checksum + Midori.Download.get_extension_for_uri (icon_uri) ?? "";
string path = Path.build_filename (get_cache_dir_for_reading (), "icons", filename);
Gdk.Pixbuf? pixbuf = new Gdk.Pixbuf.from_file_at_size (path, icon_width, icon_height);
if (pixbuf != null)
return pixbuf;
}
catch (GLib.Error error) { }
}
#endif
if (widget != null)
return widget.render_icon (Gtk.STOCK_FILE, Gtk.IconSize.MENU, null);
return null;
}
}
}

View file

@ -18,6 +18,8 @@ namespace Midori {
public class URI : Object { public class URI : Object {
public static string? parse_hostname (string? uri, out string path) { public static string? parse_hostname (string? uri, out string path) {
/* path may be null. */ /* path may be null. */
if (&path != null)
path = null;
if (uri == null) if (uri == null)
return uri; return uri;
unowned string? hostname = uri.chr (-1, '/'); unowned string? hostname = uri.chr (-1, '/');
@ -57,10 +59,22 @@ namespace Midori {
string? unescaped = GLib.Uri.unescape_string (uri, "+"); string? unescaped = GLib.Uri.unescape_string (uri, "+");
if (unescaped == null) if (unescaped == null)
return uri; return uri;
return unescaped.replace (" ", "%20"); return unescaped.replace (" ", "%20").replace ("\n", "%0A");
} }
return uri; return uri;
} }
/* Strip http(s), file and www. for tab titles or completion */
public static string strip_prefix_for_display (string uri) {
if (is_http (uri) || uri.has_prefix ("file://")) {
string stripped_uri = uri.split ("://")[1];
if (is_http (uri) && stripped_uri.has_prefix ("www."))
return stripped_uri.substring (4, -1);
return stripped_uri;
}
return uri;
}
public static string format_for_display (string? uri) { public static string format_for_display (string? uri) {
/* Percent-decode and decode puniycode for user display */ /* Percent-decode and decode puniycode for user display */
if (uri != null && uri.has_prefix ("http://")) { if (uri != null && uri.has_prefix ("http://")) {
@ -85,6 +99,9 @@ namespace Midori {
if (uri == null) if (uri == null)
return keywords; return keywords;
string escaped = GLib.Uri.escape_string (keywords, ":/", true); string escaped = GLib.Uri.escape_string (keywords, ":/", true);
/* Allow DuckDuckGo to distinguish Midori and in turn share revenue */
if (uri == "https://duckduckgo.com/?q=%s")
return "https://duckduckgo.com/?q=%s&t=midori".printf (escaped);
if (uri.str ("%s") != null) if (uri.str ("%s") != null)
return uri.printf (escaped); return uri.printf (escaped);
return uri + escaped; return uri + escaped;
@ -111,44 +128,70 @@ namespace Midori {
|| (uri.has_prefix ("geo:") && uri.chr (-1, ',') != null) || (uri.has_prefix ("geo:") && uri.chr (-1, ',') != null)
|| uri.has_prefix ("javascript:")); || uri.has_prefix ("javascript:"));
} }
public static bool is_email (string? uri) {
return uri != null
&& (uri.chr (-1, '@') != null || uri.has_prefix ("mailto:"))
/* :// and @ together would mean login credentials */
&& uri.str ("://") == null;
}
public static bool is_ip_address (string? uri) { public static bool is_ip_address (string? uri) {
/* Quick check for IPv4 or IPv6, no validation. /* Quick check for IPv4 or IPv6, no validation.
FIXME: Schemes are not handled FIXME: Schemes are not handled
hostname_is_ip_address () is not used because hostname_is_ip_address () is not used because
we'd have to separate the path from the URI first. */ we'd have to separate the path from the URI first. */
return uri != null && uri[0].isdigit () if (uri == null)
&& (uri.chr (4, '.') != null || uri.chr (4, ':') != null); return false;
/* Skip leading user/ password */
if (uri.chr (-1, '@') != null)
return is_ip_address (uri.split ("@")[1]);
/* IPv4 */
if (uri[0] != '0' && uri[0].isdigit () && (uri.chr (4, '.') != null))
return true;
/* IPv6 */
if (uri[0].isalnum () && uri[1].isalnum ()
&& uri[2].isalnum () && uri[3].isalnum () && uri[4] == ':'
&& (uri[5] == ':' || uri[5].isalnum ()))
return true;
return false;
} }
public static bool is_valid (string? uri) { public static bool is_valid (string? uri) {
return uri != null return uri != null
&& uri.chr (-1, ' ') == null && uri.chr (-1, ' ') == null
&& (URI.is_location (uri) || uri.chr (-1, '.') != null); && (URI.is_location (uri) || uri.chr (-1, '.') != null);
} }
public static string? get_folder (string uri) {
/* Base the start folder on the current view's uri if it is local */
try {
string? filename = Filename.from_uri (uri);
if (filename != null) {
string? dirname = Path.get_dirname (filename);
if (dirname != null && FileUtils.test (dirname, FileTest.IS_DIR))
return dirname;
}
}
catch (Error error) { }
return null;
}
public static GLib.ChecksumType get_fingerprint (string uri, public static GLib.ChecksumType get_fingerprint (string uri,
out string checksum, out string label) { out string checksum, out string label) {
/* http://foo.bar/baz/spam.eggs#!algo!123456 */ /* http://foo.bar/baz/spam.eggs#!algo!123456 */
unowned string display = null;
GLib.ChecksumType type = (GLib.ChecksumType)int.MAX;
unowned string delimiter = "#!md5!"; unowned string delimiter = "#!md5!";
unowned string display = _("MD5-Checksum:");
GLib.ChecksumType type = GLib.ChecksumType.MD5;
unowned string? fragment = uri.str (delimiter); unowned string? fragment = uri.str (delimiter);
if (fragment == null) { if (fragment != null) {
display = _("MD5-Checksum:");
type = GLib.ChecksumType.MD5;
}
delimiter = "#!sha1!"; delimiter = "#!sha1!";
fragment = uri.str (delimiter);
if (fragment != null) {
display = _("SHA1-Checksum:"); display = _("SHA1-Checksum:");
type = GLib.ChecksumType.SHA1; type = GLib.ChecksumType.SHA1;
fragment = uri.str (delimiter);
}
if (fragment == null) {
type = (GLib.ChecksumType)int.MAX;
display = null;
} }
/* No SHA256: no known usage and no need for strong encryption */
if (&checksum != null) if (&checksum != null)
checksum = fragment != null checksum = fragment != null
? fragment.offset (delimiter.length) : null; ? fragment.offset (delimiter.length) : null;

File diff suppressed because it is too large Load diff

View file

@ -26,115 +26,15 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#if GTK_CHECK_VERSION (2, 16, 0)
#define GtkIconEntry GtkEntry
#define GtkIconEntryPosition GtkEntryIconPosition #define GtkIconEntryPosition GtkEntryIconPosition
#define GTK_ICON_ENTRY_PRIMARY GTK_ENTRY_ICON_PRIMARY #define GTK_ICON_ENTRY_PRIMARY GTK_ENTRY_ICON_PRIMARY
#define GTK_ICON_ENTRY_SECONDARY GTK_ENTRY_ICON_SECONDARY #define GTK_ICON_ENTRY_SECONDARY GTK_ENTRY_ICON_SECONDARY
#define GTK_ICON_ENTRY GTK_ENTRY #define GTK_ICON_ENTRY GTK_ENTRY
#define GTK_TYPE_ICON_ENTRY GTK_TYPE_ENTRY
#define gtk_icon_entry_new gtk_entry_new
#define gtk_icon_entry_set_icon_from_stock gtk_entry_set_icon_from_stock #define gtk_icon_entry_set_icon_from_stock gtk_entry_set_icon_from_stock
#define gtk_icon_entry_set_icon_from_icon_name gtk_entry_set_icon_from_icon_name #define gtk_icon_entry_set_icon_from_icon_name gtk_entry_set_icon_from_icon_name
void
gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry,
GtkEntryIconPosition position,
GdkPixbuf* pixbuf);
#define gtk_icon_entry_set_tooltip gtk_entry_set_icon_tooltip_text #define gtk_icon_entry_set_tooltip gtk_entry_set_icon_tooltip_text
#define gtk_icon_entry_set_icon_highlight gtk_entry_set_icon_activatable #define gtk_icon_entry_set_icon_highlight gtk_entry_set_icon_activatable
#define gtk_icon_entry_set_progress_fraction gtk_entry_set_progress_fraction
#else
#define GTK_TYPE_ICON_ENTRY (gtk_icon_entry_get_type())
#define GTK_ICON_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_ICON_ENTRY, GtkIconEntry))
#define GTK_ICON_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_ICON_ENTRY, GtkIconEntryClass))
#define GTK_IS_ICON_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_ICON_ENTRY))
#define GTK_IS_ICON_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_ICON_ENTRY))
#define GTK_ICON_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ICON_ENTRY, GtkIconEntryClass))
typedef enum
{
GTK_ICON_ENTRY_PRIMARY,
GTK_ICON_ENTRY_SECONDARY
} GtkIconEntryPosition;
typedef struct _GtkIconEntry GtkIconEntry;
typedef struct _GtkIconEntryClass GtkIconEntryClass;
typedef struct _GtkIconEntryPrivate GtkIconEntryPrivate;
struct _GtkIconEntry
{
GtkEntry parent_object;
GtkIconEntryPrivate* priv;
};
struct _GtkIconEntryClass
{
GtkEntryClass parent_class;
/* Signals */
void (*icon_pressed) (GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
int button);
void (*icon_released) (GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
int button);
void (*gtk_reserved1) (void);
void (*gtk_reserved2) (void);
void (*gtk_reserved3) (void);
void (*gtk_reserved4) (void);
};
GType gtk_icon_entry_get_type (void) G_GNUC_CONST;
GtkWidget* gtk_icon_entry_new (void);
void gtk_icon_entry_set_icon_from_pixbuf (GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
GdkPixbuf *pixbuf);
void gtk_icon_entry_set_icon_from_stock (GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
const gchar *stock_id);
void gtk_icon_entry_set_icon_from_icon_name (GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
const gchar *icon_name);
void gtk_icon_entry_set_icon_from_gicon (const GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
GIcon *icon);
GdkPixbuf* gtk_icon_entry_get_pixbuf (const GtkIconEntry *entry,
GtkIconEntryPosition icon_pos);
GIcon* gtk_icon_entry_get_gicon (const GtkIconEntry *entry,
GtkIconEntryPosition icon_pos);
void gtk_icon_entry_set_icon_highlight (const GtkIconEntry *entry,
GtkIconEntryPosition icon_pos,
gboolean highlight);
gboolean gtk_icon_entry_get_icon_highlight (const GtkIconEntry *entry,
GtkIconEntryPosition icon_pos);
void gtk_icon_entry_set_cursor (const GtkIconEntry *icon_entry,
GtkIconEntryPosition icon_pos,
GdkCursorType cursor_type);
void gtk_icon_entry_set_tooltip (const GtkIconEntry *icon_entry,
GtkIconEntryPosition icon_pos,
const gchar *text);
void gtk_icon_entry_set_icon_sensitive (const GtkIconEntry *icon_entry,
GtkIconEntryPosition icon_pos,
gboolean sensitive);
void gtk_icon_entry_set_progress_fraction (GtkIconEntry *icon_entry,
gdouble fraction);
#endif
G_END_DECLS G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@ OBJECT:OBJECT
VOID:BOOLEAN,STRING VOID:BOOLEAN,STRING
VOID:OBJECT,ENUM,BOOLEAN VOID:OBJECT,ENUM,BOOLEAN
VOID:OBJECT,INT,INT VOID:OBJECT,INT,INT
VOID:OBJECT,OBJECT
VOID:POINTER,INT VOID:POINTER,INT
VOID:STRING,BOOLEAN VOID:STRING,BOOLEAN
VOID:STRING,INT,STRING VOID:STRING,INT,STRING

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2008-2010 Christian Dywan <christian@twotoasts.de> Copyright (C) 2008-2012 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -21,9 +21,11 @@
#include "midori-app.h" #include "midori-app.h"
#include "midori-platform.h" #include "midori-platform.h"
#include "midori-core.h"
#include <string.h> #include <string.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#if ENABLE_NLS #if ENABLE_NLS
@ -31,16 +33,7 @@
#include <locale.h> #include <locale.h>
#endif #endif
#if HAVE_HILDON #if HAVE_UNIQUE
#include <libosso.h>
#ifdef HAVE_HILDON_2_2
#include <dbus/dbus.h>
#include <mce/mode-names.h>
#include <mce/dbus-names.h>
#endif
typedef osso_context_t* MidoriAppInstance;
#define MidoriAppInstanceNull NULL
#elif HAVE_UNIQUE
typedef gpointer MidoriAppInstance; typedef gpointer MidoriAppInstance;
#define MidoriAppInstanceNull NULL #define MidoriAppInstanceNull NULL
#if defined(G_DISABLE_DEPRECATED) && !defined(G_CONST_RETURN) #if defined(G_DISABLE_DEPRECATED) && !defined(G_CONST_RETURN)
@ -64,14 +57,14 @@
#endif #endif
#endif #endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
struct _MidoriApp struct _MidoriApp
{ {
GObject parent_instance; GObject parent_instance;
MidoriBrowser* browser;
GtkAccelGroup* accel_group;
gchar* name;
MidoriWebSettings* settings; MidoriWebSettings* settings;
KatzeArray* bookmarks; KatzeArray* bookmarks;
KatzeArray* trash; KatzeArray* trash;
@ -81,13 +74,16 @@ struct _MidoriApp
KatzeArray* extensions; KatzeArray* extensions;
KatzeArray* browsers; KatzeArray* browsers;
MidoriBrowser* browser;
MidoriAppInstance instance; MidoriAppInstance instance;
#if !HAVE_HILDON || !HAVE_LIBNOTIFY #if !HAVE_LIBNOTIFY
gchar* program_notify_send; gchar* program_notify_send;
#endif #endif
}; };
static gchar* app_name = NULL;
struct _MidoriAppClass struct _MidoriAppClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
@ -216,7 +212,6 @@ _midori_app_add_browser (MidoriApp* app,
g_return_if_fail (MIDORI_IS_APP (app)); g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (MIDORI_IS_BROWSER (browser)); g_return_if_fail (MIDORI_IS_BROWSER (browser));
gtk_window_add_accel_group (GTK_WINDOW (browser), app->accel_group);
g_object_connect (browser, g_object_connect (browser,
"signal::focus-in-event", midori_browser_focus_in_event_cb, app, "signal::focus-in-event", midori_browser_focus_in_event_cb, app,
"signal::new-window", midori_browser_new_window_cb, app, "signal::new-window", midori_browser_new_window_cb, app,
@ -226,9 +221,34 @@ _midori_app_add_browser (MidoriApp* app,
NULL); NULL);
g_signal_connect_swapped (browser, "send-notification", g_signal_connect_swapped (browser, "send-notification",
G_CALLBACK (midori_app_send_notification), app); G_CALLBACK (midori_app_send_notification), app);
katze_array_add_item (app->browsers, browser); katze_array_add_item (app->browsers, browser);
#if GTK_CHECK_VERSION (3, 0, 0)
if (app->browser == NULL)
{
gchar* filename;
if ((filename = midori_paths_get_res_filename ("gtk3.css")))
{
GtkCssProvider* css_provider = gtk_css_provider_new ();
GError* error = NULL;
gtk_css_provider_load_from_path (css_provider, filename, &error);
if (error == NULL)
{
gtk_style_context_add_provider_for_screen (
gtk_widget_get_screen (GTK_WIDGET (browser)),
GTK_STYLE_PROVIDER (css_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
else
{
g_warning ("Failed to load \"%s\": %s", filename, error->message);
g_error_free (error);
}
g_free (filename);
}
}
#endif
app->browser = browser; app->browser = browser;
#if HAVE_UNIQUE #if HAVE_UNIQUE
/* We *do not* let unique watch windows because that includes /* We *do not* let unique watch windows because that includes
@ -238,10 +258,28 @@ _midori_app_add_browser (MidoriApp* app,
#endif #endif
} }
#ifdef HAVE_SIGNAL_H
static MidoriApp* app_singleton;
static void
midori_app_signal_handler (int signal_id)
{
signal (signal_id, 0);
if (!midori_paths_is_readonly ())
midori_app_quit (app_singleton);
if (kill (getpid (), signal_id))
exit (1);
}
#endif
static void static void
_midori_app_quit (MidoriApp* app) _midori_app_quit (MidoriApp* app)
{ {
gtk_main_quit (); if (!midori_paths_is_readonly ())
{
gchar* config_file = midori_paths_get_config_filename_for_writing ("running");
g_unlink (config_file);
g_free (config_file);
}
} }
static void static void
@ -470,8 +508,7 @@ midori_app_command_received (MidoriApp* app,
{ {
MidoriBrowser* browser = midori_app_create_browser (app); MidoriBrowser* browser = midori_app_create_browser (app);
midori_app_add_browser (app, browser); midori_app_add_browser (app, browser);
/* FIXME: Should open the homepage according to settings */ midori_browser_add_uri (browser, "about:home");
midori_browser_add_uri (browser, "");
midori_browser_activate_action (browser, "Location"); midori_browser_activate_action (browser, "Location");
gtk_widget_show (GTK_WIDGET (browser)); gtk_widget_show (GTK_WIDGET (browser));
midori_app_raise_window (GTK_WINDOW (browser), screen); midori_app_raise_window (GTK_WINDOW (browser), screen);
@ -517,21 +554,12 @@ midori_app_command_received (MidoriApp* app,
else else
{ {
/* Switch to already open tab if possible */ /* Switch to already open tab if possible */
guint i = 0; KatzeArray* items = midori_browser_get_proxy_array (browser);
GtkWidget* tab; KatzeItem* found = katze_array_find_uri (items, fixed_uri);
gboolean found = FALSE; if (found != NULL)
while ((tab = midori_browser_get_nth_tab (browser, i++))) midori_browser_set_current_item (browser, found);
if (g_str_equal (
midori_view_get_display_uri (MIDORI_VIEW (tab)),
fixed_uri))
{
found = TRUE;
break;
}
if (found)
midori_browser_set_current_tab (browser, tab);
else else
midori_browser_set_current_page (browser, midori_browser_set_current_tab (browser,
midori_browser_add_uri (browser, fixed_uri)); midori_browser_add_uri (browser, fixed_uri));
} }
} }
@ -543,51 +571,18 @@ midori_app_command_received (MidoriApp* app,
} }
else if (g_str_equal (command, "command")) else if (g_str_equal (command, "command"))
{ {
guint i = 0;
if (!uris || !app->browser) if (!uris || !app->browser)
return FALSE; return FALSE;
while (uris[i] != NULL) gint i;
{ for (i = 0; uris && uris[i]; i++)
midori_browser_activate_action (app->browser, uris[i]); midori_browser_activate_action (app->browser, uris[i]);
i++;
}
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
} }
#if HAVE_HILDON #if HAVE_UNIQUE
static osso_return_t
midori_app_osso_rpc_handler_cb (const gchar* interface,
const gchar* method,
GArray* arguments,
gpointer data,
osso_rpc_t * retval)
{
MidoriApp* app = MIDORI_APP (data);
GdkScreen* screen = NULL;
gboolean success;
if (!g_strcmp0 (method, "top_application"))
success = midori_app_command_received (app, "activate", NULL, screen);
else if (!g_strcmp0 (method, "new"))
success = midori_app_command_received (app, "new", NULL, screen);
else if (!g_strcmp0 (method, "open"))
{
/* FIXME: Handle arguments */
success = midori_app_command_received (app, "open", NULL, screen);
}
else if (!g_strcmp0 (method, "command"))
{
/* FIXME: Handle arguments */
success = midori_app_command_received (app, "command", NULL, screen);
}
return success ? OSSO_OK : OSSO_INVALID;
}
#elif HAVE_UNIQUE
static UniqueResponse static UniqueResponse
midori_browser_message_received_cb (UniqueApp* instance, midori_browser_message_received_cb (UniqueApp* instance,
gint command, gint command,
@ -687,58 +682,42 @@ static MidoriAppInstance
midori_app_create_instance (MidoriApp* app) midori_app_create_instance (MidoriApp* app)
{ {
MidoriAppInstance instance; MidoriAppInstance instance;
#if HAVE_HILDON
instance = osso_initialize (PACKAGE_NAME, PACKAGE_VERSION, FALSE, NULL);
if (!instance)
{
g_critical ("Error initializing OSSO D-Bus context - Midori");
return NULL;
}
if (osso_rpc_set_default_cb_f (instance, midori_app_osso_rpc_handler_cb,
app) != OSSO_OK)
{
g_critical ("Error initializing remote procedure call handler - Midori");
osso_deinitialize (instance);
return NULL;
}
#ifdef HAVE_HILDON_2_2
if (OSSO_OK == osso_rpc_run_system (instance, MCE_SERVICE, MCE_REQUEST_PATH,
MCE_REQUEST_IF, MCE_ACCELEROMETER_ENABLE_REQ, NULL, DBUS_TYPE_INVALID))
/* Accelerometer enabled */;
#endif
#else
GdkDisplay* display; GdkDisplay* display;
gchar* display_name; gchar* display_name;
gchar* instance_name; gchar* instance_name;
guint i, n;
#if !HAVE_UNIQUE #if !HAVE_UNIQUE
gboolean exists; gboolean exists;
GIOChannel* channel; GIOChannel* channel;
#endif #endif
if (!app->name)
{
const gchar* config = sokoke_set_config_dir (NULL);
gchar* name_hash;
name_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, config, -1);
app->name = g_strconcat ("midori", "_", name_hash, NULL);
g_free (name_hash);
g_object_notify (G_OBJECT (app), "name");
}
if (!(display = gdk_display_get_default ())) if (!(display = gdk_display_get_default ()))
return MidoriAppInstanceNull; return MidoriAppInstanceNull;
{
#if HAVE_UNIQUE
const gchar* config = midori_paths_get_config_dir_for_reading ();
gchar* config_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, config, -1);
gchar* name_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, app_name, -1);
katze_assign (app_name, g_strconcat (PACKAGE_NAME,
"_", config_hash, "_", name_hash, NULL));
g_free (config_hash);
g_free (name_hash);
#else
katze_assign (app_name, g_strdup (PACKAGE_NAME));
#endif
g_object_notify (G_OBJECT (app), "name");
}
#ifdef GDK_WINDOWING_X11
/* On X11: :0 or :0.0 which is equivalent */
display_name = g_strndup (gdk_display_get_name (display), 2);
#else
display_name = g_strdup (gdk_display_get_name (display)); display_name = g_strdup (gdk_display_get_name (display));
n = strlen (display_name); #endif
for (i = 0; i < n; i++) g_strdelimit (display_name, ":.\\/", '_');
if (strchr (":.\\/", display_name[i])) instance_name = g_strdup_printf ("de.twotoasts.%s_%s", app_name, display_name);
display_name[i] = '_'; g_free (display_name);
instance_name = g_strdup_printf ("de.twotoasts.%s_%s", app->name, display_name); katze_assign (app_name, instance_name);
#if HAVE_UNIQUE #if HAVE_UNIQUE
instance = unique_app_new (instance_name, NULL); instance = unique_app_new (instance_name, NULL);
@ -746,7 +725,7 @@ midori_app_create_instance (MidoriApp* app)
g_signal_connect (instance, "message-received", g_signal_connect (instance, "message-received",
G_CALLBACK (midori_browser_message_received_cb), app); G_CALLBACK (midori_browser_message_received_cb), app);
#else #else
instance = socket_init (instance_name, sokoke_set_config_dir (NULL), &exists); instance = socket_init (instance_name, midori_paths_get_config_dir_for_writing (), &exists);
g_object_set_data (G_OBJECT (app), "sock-exists", g_object_set_data (G_OBJECT (app), "sock-exists",
exists ? (gpointer)0xdeadbeef : NULL); exists ? (gpointer)0xdeadbeef : NULL);
if (instance != MidoriAppInstanceNull) if (instance != MidoriAppInstanceNull)
@ -755,19 +734,53 @@ midori_app_create_instance (MidoriApp* app)
g_io_add_watch (channel, G_IO_IN | G_IO_PRI | G_IO_ERR, g_io_add_watch (channel, G_IO_IN | G_IO_PRI | G_IO_ERR,
(GIOFunc)midori_app_io_channel_watch_cb, app); (GIOFunc)midori_app_io_channel_watch_cb, app);
} }
#endif
g_free (instance_name);
g_free (display_name);
#endif #endif
return instance; return instance;
} }
const gchar*
midori_app_get_name (MidoriApp* app)
{
return app_name;
}
gboolean
midori_app_get_crashed (MidoriApp* app)
{
if (!midori_paths_is_readonly ())
{
/* We test for the presence of a dummy file which is created once
and deleted during normal runtime, but persists in case of a crash. */
gchar* config_file = midori_paths_get_config_filename_for_writing ("running");
gboolean crashed = (g_access (config_file, F_OK) == 0);
if (!crashed)
g_file_set_contents (config_file, "RUNNING", -1, NULL);
g_free (config_file);
if (crashed)
return TRUE;
}
return FALSE;
}
static void static void
midori_app_init (MidoriApp* app) midori_app_init (MidoriApp* app)
{ {
app->accel_group = gtk_accel_group_new (); #ifdef HAVE_SIGNAL_H
app_singleton = app;
#ifdef SIGHUP
signal (SIGHUP, &midori_app_signal_handler);
#endif
#ifdef SIGINT
signal (SIGINT, &midori_app_signal_handler);
#endif
#ifdef SIGTERM
signal (SIGTERM, &midori_app_signal_handler);
#endif
#ifdef SIGQUIT
signal (SIGQUIT, &midori_app_signal_handler);
#endif
#endif
app->settings = NULL; app->settings = NULL;
app->bookmarks = NULL; app->bookmarks = NULL;
@ -775,17 +788,16 @@ midori_app_init (MidoriApp* app)
app->search_engines = NULL; app->search_engines = NULL;
app->history = NULL; app->history = NULL;
app->speeddial = NULL; app->speeddial = NULL;
app->extensions = NULL; app->extensions = katze_array_new (KATZE_TYPE_ARRAY);
app->browsers = katze_array_new (MIDORI_TYPE_BROWSER); app->browsers = katze_array_new (MIDORI_TYPE_BROWSER);
app->instance = MidoriAppInstanceNull; app->instance = MidoriAppInstanceNull;
#if HAVE_LIBNOTIFY #if HAVE_LIBNOTIFY
notify_init ("midori"); notify_init (PACKAGE_NAME);
#else #else
app->program_notify_send = g_find_program_in_path ("notify-send"); app->program_notify_send = g_find_program_in_path ("notify-send");
#endif #endif
} }
static void static void
@ -793,9 +805,7 @@ midori_app_finalize (GObject* object)
{ {
MidoriApp* app = MIDORI_APP (object); MidoriApp* app = MIDORI_APP (object);
g_object_unref (app->accel_group); katze_assign (app_name, NULL);
katze_assign (app->name, NULL);
katze_object_assign (app->settings, NULL); katze_object_assign (app->settings, NULL);
katze_object_assign (app->bookmarks, NULL); katze_object_assign (app->bookmarks, NULL);
katze_object_assign (app->trash, NULL); katze_object_assign (app->trash, NULL);
@ -805,10 +815,7 @@ midori_app_finalize (GObject* object)
katze_object_assign (app->extensions, NULL); katze_object_assign (app->extensions, NULL);
katze_object_assign (app->browsers, NULL); katze_object_assign (app->browsers, NULL);
#if HAVE_HILDON #if HAVE_UNIQUE
osso_deinitialize (app->instance);
app->instance = NULL;
#elif HAVE_UNIQUE
katze_object_assign (app->instance, NULL); katze_object_assign (app->instance, NULL);
#else #else
sock_cleanup (); sock_cleanup ();
@ -835,7 +842,7 @@ midori_app_set_property (GObject* object,
switch (prop_id) switch (prop_id)
{ {
case PROP_NAME: case PROP_NAME:
katze_assign (app->name, g_value_dup_string (value)); katze_assign (app_name, g_value_dup_string (value));
break; break;
case PROP_SETTINGS: case PROP_SETTINGS:
katze_object_assign (app->settings, g_value_dup_object (value)); katze_object_assign (app->settings, g_value_dup_object (value));
@ -875,7 +882,7 @@ midori_app_get_property (GObject* object,
switch (prop_id) switch (prop_id)
{ {
case PROP_NAME: case PROP_NAME:
g_value_set_string (value, app->name); g_value_set_string (value, app_name);
break; break;
case PROP_SETTINGS: case PROP_SETTINGS:
g_value_set_object (value, app->settings); g_value_set_object (value, app->settings);
@ -918,12 +925,38 @@ midori_app_get_property (GObject* object,
* Return value: a new #MidoriApp * Return value: a new #MidoriApp
**/ **/
MidoriApp* MidoriApp*
midori_app_new (void) midori_app_new (const gchar* name)
{ {
MidoriApp* app = g_object_new (MIDORI_TYPE_APP, return g_object_new (MIDORI_TYPE_APP, "name", name, NULL);
NULL); }
return app; /**
* midori_app_new_proxy:
* @app: a #MidoriApp, or %NULL
*
* Instantiates a proxy #MidoriApp that can be passed to untrusted code
* or for sensitive use cases. Properties can be freely changed.
*
* Return value: a new #MidoriApp
*
* Since: 0.5.0
**/
MidoriApp*
midori_app_new_proxy (MidoriApp* app)
{
g_return_val_if_fail (MIDORI_IS_APP (app) || !app, NULL);
return midori_app_new (NULL);
}
static gboolean instance_is_not_running = FALSE;
static gboolean instance_is_running = FALSE;
void
midori_app_set_instance_is_running (gboolean is_running)
{
instance_is_not_running = !is_running;
instance_is_running = is_running;
} }
/** /**
@ -943,20 +976,19 @@ midori_app_instance_is_running (MidoriApp* app)
{ {
g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); g_return_val_if_fail (MIDORI_IS_APP (app), FALSE);
if (instance_is_not_running)
return FALSE;
else if (instance_is_running)
return TRUE;
if (app->instance == MidoriAppInstanceNull) if (app->instance == MidoriAppInstanceNull)
app->instance = midori_app_create_instance (app); app->instance = midori_app_create_instance (app);
#if HAVE_HILDON #if HAVE_UNIQUE
/* FIXME: Determine if application is running already */ return app->instance && unique_app_is_running (app->instance);
if (app->instance)
return FALSE;
#elif HAVE_UNIQUE
if (app->instance)
return unique_app_is_running (app->instance);
#else #else
return g_object_get_data (G_OBJECT (app), "sock-exists") != NULL; return g_object_get_data (G_OBJECT (app), "sock-exists") != NULL;
#endif #endif
return FALSE;
} }
/** /**
@ -973,19 +1005,13 @@ midori_app_instance_is_running (MidoriApp* app)
gboolean gboolean
midori_app_instance_send_activate (MidoriApp* app) midori_app_instance_send_activate (MidoriApp* app)
{ {
#if HAVE_UNIQUE g_return_val_if_fail (MIDORI_IS_APP (app), FALSE);
UniqueResponse response;
#endif
/* g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); */
g_return_val_if_fail (midori_app_instance_is_running (app), FALSE); g_return_val_if_fail (midori_app_instance_is_running (app), FALSE);
#if HAVE_HILDON #if HAVE_UNIQUE
osso_application_top (app->instance, PACKAGE_NAME, NULL);
#elif HAVE_UNIQUE
if (app->instance) if (app->instance)
{ {
response = unique_app_send_message (app->instance, UNIQUE_ACTIVATE, NULL); UniqueResponse response = unique_app_send_message (app->instance, UNIQUE_ACTIVATE, NULL);
if (response == UNIQUE_RESPONSE_OK) if (response == UNIQUE_RESPONSE_OK)
return TRUE; return TRUE;
} }
@ -1011,19 +1037,13 @@ midori_app_instance_send_activate (MidoriApp* app)
gboolean gboolean
midori_app_instance_send_new_browser (MidoriApp* app) midori_app_instance_send_new_browser (MidoriApp* app)
{ {
#if HAVE_UNIQUE g_return_val_if_fail (MIDORI_IS_APP (app), FALSE);
UniqueResponse response;
#endif
/* g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); */
g_return_val_if_fail (midori_app_instance_is_running (app), FALSE); g_return_val_if_fail (midori_app_instance_is_running (app), FALSE);
#if HAVE_HILDON #if HAVE_UNIQUE
osso_application_top (app->instance, PACKAGE_NAME, "new");
#elif HAVE_UNIQUE
if (app->instance) if (app->instance)
{ {
response = unique_app_send_message (app->instance, UNIQUE_NEW, NULL); UniqueResponse response = unique_app_send_message (app->instance, UNIQUE_NEW, NULL);
if (response == UNIQUE_RESPONSE_OK) if (response == UNIQUE_RESPONSE_OK)
return TRUE; return TRUE;
} }
@ -1053,20 +1073,26 @@ gboolean
midori_app_instance_send_uris (MidoriApp* app, midori_app_instance_send_uris (MidoriApp* app,
gchar** uris) gchar** uris)
{ {
#if HAVE_UNIQUE g_return_val_if_fail (MIDORI_IS_APP (app), FALSE);
UniqueMessageData* message;
UniqueResponse response;
#endif
/* g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); */
g_return_val_if_fail (midori_app_instance_is_running (app), FALSE); g_return_val_if_fail (midori_app_instance_is_running (app), FALSE);
g_return_val_if_fail (uris != NULL, FALSE); g_return_val_if_fail (uris != NULL, FALSE);
#if HAVE_HILDON #if HAVE_UNIQUE
/* FIXME: Implement */
#elif HAVE_UNIQUE
if (app->instance) if (app->instance)
{ {
UniqueMessageData* message;
UniqueResponse response;
/* Encode any IDN addresses because libUnique doesn't like them */
int i = 0;
while (uris[i] != NULL)
{
gchar* new_uri = sokoke_magic_uri (uris[i], TRUE, TRUE);
gchar* escaped_uri = g_uri_escape_string (new_uri, NULL, FALSE);
g_free (new_uri);
katze_assign (uris[i], escaped_uri);
i++;
}
message = unique_message_data_new (); message = unique_message_data_new ();
unique_message_data_set_uris (message, uris); unique_message_data_set_uris (message, uris);
response = unique_app_send_message (app->instance, UNIQUE_OPEN, message); response = unique_app_send_message (app->instance, UNIQUE_OPEN, message);
@ -1103,23 +1129,24 @@ gboolean
midori_app_send_command (MidoriApp* app, midori_app_send_command (MidoriApp* app,
gchar** command) gchar** command)
{ {
#if HAVE_UNIQUE g_return_val_if_fail (MIDORI_IS_APP (app), FALSE);
UniqueMessageData* message;
UniqueResponse response;
#endif
/* g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); */
g_return_val_if_fail (command != NULL, FALSE); g_return_val_if_fail (command != NULL, FALSE);
if (!midori_app_instance_is_running (app)) if (!midori_app_instance_is_running (app))
{
MidoriBrowser* browser = midori_browser_new ();
int i;
for (i=0; command && command[i]; i++)
midori_browser_assert_action (browser, command[i]);
gtk_widget_destroy (GTK_WIDGET (browser));
return midori_app_command_received (app, "command", command, NULL); return midori_app_command_received (app, "command", command, NULL);
}
#if HAVE_HILDON #if HAVE_UNIQUE
/* FIXME: Implement */
#elif HAVE_UNIQUE
if (app->instance) if (app->instance)
{ {
message = unique_message_data_new (); UniqueResponse response;
UniqueMessageData* message = unique_message_data_new ();
unique_message_data_set_uris (message, command); unique_message_data_set_uris (message, command);
response = unique_app_send_message (app->instance, response = unique_app_send_message (app->instance,
MIDORI_UNIQUE_COMMAND, message); MIDORI_UNIQUE_COMMAND, message);
@ -1160,6 +1187,17 @@ midori_app_add_browser (MidoriApp* app,
g_signal_emit (app, signals[ADD_BROWSER], 0, browser); g_signal_emit (app, signals[ADD_BROWSER], 0, browser);
} }
void
midori_app_set_browsers (MidoriApp* app,
KatzeArray* browsers,
MidoriBrowser* browser)
{
g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (KATZE_IS_ARRAY (browsers));
katze_object_assign (app->browsers, g_object_ref (browsers));
app->browser = browser;
}
/** /**
* midori_app_create_browser: * midori_app_create_browser:
* @app: a #MidoriApp * @app: a #MidoriApp
@ -1263,17 +1301,13 @@ midori_app_send_notification (MidoriApp* app,
g_return_if_fail (MIDORI_IS_APP (app)); g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (title); g_return_if_fail (title);
#if HAVE_HILDON #if HAVE_LIBNOTIFY
hildon_banner_show_information_with_markup (GTK_WIDGET (app->browser),
"midori", message);
#elif HAVE_LIBNOTIFY
if (notify_is_initted ()) if (notify_is_initted ())
{ {
NotifyNotification* note;
#if NOTIFY_CHECK_VERSION (0, 7, 0) #if NOTIFY_CHECK_VERSION (0, 7, 0)
note = notify_notification_new (title, message, "midori"); NotifyNotification* note = notify_notification_new (title, message, "midori");
#else #else
note = notify_notification_new (title, message, "midori", NULL); NotifyNotification* note = notify_notification_new (title, message, "midori", NULL);
#endif #endif
notify_notification_show (note, NULL); notify_notification_show (note, NULL);
g_object_unref (note); g_object_unref (note);
@ -1305,143 +1339,38 @@ midori_app_send_notification (MidoriApp* app,
* Since: 0.4.2 * Since: 0.4.2
**/ **/
void void
midori_app_setup (gchar** argument_vector) midori_app_setup (gint *argc,
gchar** *argument_vector,
const GOptionEntry *entries)
{ {
GtkIconSource* icon_source; GtkIconSource* icon_source;
GtkIconSet* icon_set; GtkIconSet* icon_set;
GtkIconFactory* factory; GtkIconFactory* factory;
gsize i; gsize i;
GError* error = NULL;
gboolean success;
typedef struct static GtkStockItem items[] =
{ {
const gchar* stock_id; { STOCK_IMAGE },
const gchar* label; { MIDORI_STOCK_WEB_BROWSER },
GdkModifierType modifier; { STOCK_NEWS_FEED },
guint keyval; { STOCK_STYLE },
const gchar* fallback;
} FatStockItem;
static FatStockItem items[] =
{
{ STOCK_EXTENSION, NULL, 0, 0, GTK_STOCK_CONVERT },
{ STOCK_IMAGE, NULL, 0, 0, GTK_STOCK_ORIENTATION_PORTRAIT },
{ STOCK_WEB_BROWSER, NULL, 0, 0, "gnome-web-browser" },
{ STOCK_NEWS_FEED, NULL, 0, 0, GTK_STOCK_INDEX },
{ STOCK_SCRIPT, NULL, 0, 0, GTK_STOCK_EXECUTE },
{ STOCK_STYLE, NULL, 0, 0, GTK_STOCK_SELECT_COLOR },
{ STOCK_TRANSFER, NULL, 0, 0, GTK_STOCK_SAVE },
{ STOCK_BOOKMARK, N_("_Bookmark"), 0, 0, GTK_STOCK_FILE }, { STOCK_BOOKMARKS, N_("_Bookmarks"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_B },
{ STOCK_BOOKMARKS, N_("_Bookmarks"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_B, GTK_STOCK_DIRECTORY }, { STOCK_BOOKMARK_ADD, N_("Add Boo_kmark") },
{ STOCK_BOOKMARK_ADD, N_("Add Boo_kmark"), 0, 0, "stock_add-bookmark" }, { STOCK_EXTENSION, N_("_Extensions") },
{ STOCK_CONSOLE, N_("_Console"), 0, 0, GTK_STOCK_DIALOG_WARNING }, { STOCK_HISTORY, N_("_History"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_H },
{ STOCK_EXTENSIONS, N_("_Extensions"), 0, 0, GTK_STOCK_CONVERT }, { STOCK_SCRIPT, N_("_Userscripts") },
{ STOCK_HISTORY, N_("_History"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_H, GTK_STOCK_SORT_ASCENDING }, { STOCK_STYLE, N_("User_styles") },
{ STOCK_HOMEPAGE, N_("_Homepage"), 0, 0, GTK_STOCK_HOME }, { STOCK_TAB_NEW, N_("New _Tab") },
{ STOCK_SCRIPTS, N_("_Userscripts"), 0, 0, GTK_STOCK_EXECUTE }, { MIDORI_STOCK_TRANSFER, N_("_Transfers"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_J },
{ STOCK_TAB_NEW, N_("New _Tab"), 0, 0, GTK_STOCK_ADD }, { MIDORI_STOCK_PLUGINS, N_("Netscape p_lugins") },
{ STOCK_TRANSFERS, N_("_Transfers"), GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_J, GTK_STOCK_SAVE }, { STOCK_USER_TRASH, N_("_Closed Tabs") },
{ STOCK_PLUGINS, N_("Netscape p_lugins"), 0, 0, GTK_STOCK_CONVERT }, { STOCK_WINDOW_NEW, N_("New _Window") },
{ STOCK_USER_TRASH, N_("_Closed Tabs"), 0, 0, "gtk-undo-ltr" }, { STOCK_FOLDER_NEW, N_("New _Folder") },
{ STOCK_WINDOW_NEW, N_("New _Window"), 0, 0, GTK_STOCK_ADD },
{ GTK_STOCK_DIRECTORY, N_("New _Folder"), 0, 0, NULL },
}; };
/* Preserve argument vector */
sokoke_get_argv (argument_vector);
/* libSoup uses threads, therefore if WebKit is built with libSoup
* or Midori is using it, we need to initialize threads. */
if (!g_thread_supported ()) g_thread_init (NULL);
#if ENABLE_NLS
setlocale (LC_ALL, "");
if (g_getenv ("MIDORI_NLSPATH"))
bindtextdomain (GETTEXT_PACKAGE, g_getenv ("MIDORI_NLSPATH"));
else
#ifdef G_OS_WIN32
{
gchar* path = sokoke_find_data_filename ("locale", FALSE);
bindtextdomain (GETTEXT_PACKAGE, path);
g_free (path);
}
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#endif
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
g_type_init ();
factory = gtk_icon_factory_new ();
for (i = 0; i < G_N_ELEMENTS (items); i++)
{
icon_set = gtk_icon_set_new ();
icon_source = gtk_icon_source_new ();
if (items[i].fallback)
{
gtk_icon_source_set_icon_name (icon_source, items[i].fallback);
items[i].fallback = NULL;
gtk_icon_set_add_source (icon_set, icon_source);
}
gtk_icon_source_set_icon_name (icon_source, items[i].stock_id);
gtk_icon_set_add_source (icon_set, icon_source);
gtk_icon_source_free (icon_source);
gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
gtk_icon_set_unref (icon_set);
}
gtk_stock_add_static ((GtkStockItem*)items, G_N_ELEMENTS (items));
gtk_icon_factory_add_default (factory);
g_object_unref (factory);
#if HAVE_HILDON
/* Maemo doesn't theme stock icons. So we map platform icons
to stock icons. These are all monochrome toolbar icons. */
typedef struct
{
const gchar* stock_id;
const gchar* icon_name;
} CompatItem;
static CompatItem compat_items[] =
{
{ GTK_STOCK_ADD, "general_add" },
{ GTK_STOCK_BOLD, "general_bold" },
{ GTK_STOCK_CLOSE, "general_close_b" },
{ GTK_STOCK_DELETE, "general_delete" },
{ GTK_STOCK_DIRECTORY, "general_toolbar_folder" },
{ GTK_STOCK_FIND, "general_search" },
{ GTK_STOCK_FULLSCREEN, "general_fullsize_b" },
{ GTK_STOCK_GO_BACK, "general_back" },
{ GTK_STOCK_GO_FORWARD, "general_forward" },
{ GTK_STOCK_GO_UP, "filemanager_folder_up" },
{ GTK_STOCK_GOTO_FIRST, "pdf_viewer_first_page" },
{ GTK_STOCK_GOTO_LAST, "pdf_viewer_last_page" },
{ GTK_STOCK_INFO, "general_information" },
{ GTK_STOCK_ITALIC, "general_italic" },
{ GTK_STOCK_JUMP_TO, "general_move_to_folder" },
{ GTK_STOCK_PREFERENCES,"general_settings" },
{ GTK_STOCK_REFRESH, "general_refresh" },
{ GTK_STOCK_SAVE, "notes_save" },
{ GTK_STOCK_STOP, "general_stop" },
{ GTK_STOCK_UNDERLINE, "notes_underline" },
{ GTK_STOCK_ZOOM_IN, "pdf_zoomin" },
{ GTK_STOCK_ZOOM_OUT, "pdf_zoomout" },
};
factory = gtk_icon_factory_new ();
for (i = 0; i < G_N_ELEMENTS (compat_items); i++)
{
icon_set = gtk_icon_set_new ();
icon_source = gtk_icon_source_new ();
gtk_icon_source_set_icon_name (icon_source, compat_items[i].icon_name);
gtk_icon_set_add_source (icon_set, icon_source);
gtk_icon_source_free (icon_source);
gtk_icon_factory_add (factory, compat_items[i].stock_id, icon_set);
gtk_icon_set_unref (icon_set);
}
gtk_icon_factory_add_default (factory);
g_object_unref (factory);
#endif
/* Print messages to stdout on Win32 console, cf. AbiWord /* Print messages to stdout on Win32 console, cf. AbiWord
* http://svn.abisource.com/abiword/trunk/src/wp/main/win/Win32Main.cpp */ * http://svn.abisource.com/abiword/trunk/src/wp/main/win/Win32Main.cpp */
#ifdef _WIN32 #ifdef _WIN32
@ -1464,5 +1393,117 @@ midori_app_setup (gchar** argument_vector)
} }
} }
#endif #endif
/* libSoup uses threads, therefore if WebKit is built with libSoup
* or Midori is using it, we need to initialize threads. */
#if !GLIB_CHECK_VERSION (2, 32, 0)
if (!g_thread_supported ()) g_thread_init (NULL);
#endif
/* Midori.Paths uses GFile */
g_type_init ();
/* Preserve argument vector */
midori_paths_init_exec_path (*argument_vector, *argc);
#if ENABLE_NLS
if (g_getenv ("MIDORI_NLSPATH"))
bindtextdomain (GETTEXT_PACKAGE, g_getenv ("MIDORI_NLSPATH"));
else
#ifdef G_OS_WIN32
{
gchar* path = midori_paths_get_data_filename ("locale", FALSE);
bindtextdomain (GETTEXT_PACKAGE, path);
g_free (path);
}
#else
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#endif
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
#ifdef HAVE_GRANITE_CLUTTER
success = gtk_clutter_init_with_args (argc, argument_vector, _("[Addresses]"),
(GOptionEntry*)entries, GETTEXT_PACKAGE, &error);
#elif GTK_CHECK_VERSION (3, 0, 0)
success = gtk_init_with_args (argc, argument_vector, _("[Addresses]"),
entries, GETTEXT_PACKAGE, &error);
#else
success = gtk_init_with_args (argc, argument_vector, _("[Addresses]"),
(GOptionEntry*)entries, GETTEXT_PACKAGE, &error);
#endif
factory = gtk_icon_factory_new ();
for (i = 0; i < G_N_ELEMENTS (items); i++)
{
icon_set = gtk_icon_set_new ();
icon_source = gtk_icon_source_new ();
gtk_icon_source_set_icon_name (icon_source, items[i].stock_id);
gtk_icon_set_add_source (icon_set, icon_source);
gtk_icon_source_free (icon_source);
gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
gtk_icon_set_unref (icon_set);
}
gtk_stock_add_static ((GtkStockItem*)items, G_N_ELEMENTS (items));
gtk_icon_factory_add_default (factory);
g_object_unref (factory);
if (!success)
midori_error (error->message);
}
void
midori_error (const gchar* format,
...)
{
g_printerr ("%s - ", g_get_application_name ());
va_list args;
va_start (args, format);
g_vfprintf (stderr, format, args);
va_end (args);
g_printerr ("\n");
exit (1);
}
gboolean
midori_debug (const gchar* token)
{
static const gchar* debug_token = NULL;
const gchar* debug_tokens = "headers body referer cookies paths hsts unarmed bookmarks ";
const gchar* full_debug_tokens = "adblock:match adblock:time startup ";
if (debug_token == NULL)
{
gchar* found_token;
const gchar* debug = g_getenv ("MIDORI_DEBUG");
const gchar* legacy_touchscreen = g_getenv ("MIDORI_TOUCHSCREEN");
if (legacy_touchscreen && *legacy_touchscreen)
g_warning ("MIDORI_TOUCHSCREEN is obsolete: "
"GTK+ 3.4 enables touchscreens automatically, "
"older GTK+ versions aren't supported as of Midori 0.4.9");
if (debug && (found_token = strstr (full_debug_tokens, debug)) && *(found_token + strlen (debug)) == ' ')
{
#ifdef G_ENABLE_DEBUG
debug_token = g_intern_static_string (debug);
#else
g_warning ("Value '%s' for MIDORI_DEBUG requires a full debugging build.", debug);
#endif
}
else if (debug && (found_token = strstr (debug_tokens, debug)) && *(found_token + strlen (debug)) == ' ')
debug_token = g_intern_static_string (debug);
else if (debug)
g_warning ("Unrecognized value '%s' for MIDORI_DEBUG.", debug);
else
debug_token = "NONE";
if (!debug_token)
{
debug_token = "INVALID";
g_print ("Supported values: %s\nWith full debugging: %s\n",
debug_tokens, full_debug_tokens);
}
}
if (debug_token != g_intern_static_string ("NONE")
&& !strstr (debug_tokens, token) && !strstr (full_debug_tokens, token))
g_warning ("Token '%s' passed to midori_debug is not a known token.", token);
return debug_token == g_intern_static_string (token);
} }

View file

@ -39,7 +39,19 @@ GType
midori_app_get_type (void) G_GNUC_CONST; midori_app_get_type (void) G_GNUC_CONST;
MidoriApp* MidoriApp*
midori_app_new (void); midori_app_new (const gchar* name);
MidoriApp*
midori_app_new_proxy (MidoriApp* app);
const gchar*
midori_app_get_name (MidoriApp* app);
gboolean
midori_app_get_crashed (MidoriApp* app);
void
midori_app_set_instance_is_running(gboolean is_running);
gboolean gboolean
midori_app_instance_is_running (MidoriApp* app); midori_app_instance_is_running (MidoriApp* app);
@ -80,7 +92,16 @@ midori_app_send_notification (MidoriApp* app,
const gchar* message); const gchar* message);
void void
midori_app_setup (gchar** argument_vector); midori_app_setup (gint *argc,
gchar** *argument_vector,
const GOptionEntry *entries);
gboolean
midori_debug (const gchar* token);
void
midori_error (const gchar* format,
...);
G_END_DECLS G_END_DECLS

View file

@ -35,7 +35,8 @@ katze_xbel_parse_info (KatzeItem* item,
xmlNodePtr cur); xmlNodePtr cur);
static gchar* static gchar*
katze_item_metadata_to_xbel (KatzeItem* item); katze_item_metadata_to_xbel (KatzeItem* item,
gboolean tiny_xbel);
#if HAVE_LIBXML #if HAVE_LIBXML
static KatzeItem* static KatzeItem*
@ -50,9 +51,17 @@ katze_item_from_xmlNodePtr (xmlNodePtr cur)
while (cur) while (cur)
{ {
if (katze_str_equal ((gchar*)cur->name, "title")) if (katze_str_equal ((gchar*)cur->name, "title"))
item->name = g_strstrip ((gchar*)xmlNodeGetContent (cur)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
katze_item_set_name (item, value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur->name, "desc")) else if (katze_str_equal ((gchar*)cur->name, "desc"))
item->text = g_strstrip ((gchar*)xmlNodeGetContent (cur)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
katze_item_set_text (item, value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur->name, "info")) else if (katze_str_equal ((gchar*)cur->name, "info"))
katze_xbel_parse_info (item, cur); katze_xbel_parse_info (item, cur);
cur = cur->next; cur = cur->next;
@ -86,9 +95,17 @@ katze_array_from_xmlNodePtr (xmlNodePtr cur)
while (cur) while (cur)
{ {
if (katze_str_equal ((gchar*)cur->name, "title")) if (katze_str_equal ((gchar*)cur->name, "title"))
((KatzeItem*)array)->name = g_strstrip ((gchar*)xmlNodeGetContent (cur)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
katze_item_set_text (KATZE_ITEM (array), value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur->name, "desc")) else if (katze_str_equal ((gchar*)cur->name, "desc"))
((KatzeItem*)array)->text = g_strstrip ((gchar*)xmlNodeGetContent (cur)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur));
katze_item_set_name (KATZE_ITEM (array), value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur->name, "info")) else if (katze_str_equal ((gchar*)cur->name, "info"))
katze_xbel_parse_info ((KatzeItem*)array, cur); katze_xbel_parse_info ((KatzeItem*)array, cur);
else if (katze_str_equal ((gchar*)cur->name, "folder")) else if (katze_str_equal ((gchar*)cur->name, "folder"))
@ -187,13 +204,12 @@ katze_xbel_parse_info (KatzeItem* item,
/* Loads the contents from an xmlNodePtr into an array. */ /* Loads the contents from an xmlNodePtr into an array. */
static gboolean static gboolean
katze_array_from_xmlDocPtr (KatzeArray* array, katze_array_from_xmlDocPtr (KatzeArray* array,
gboolean tiny_xbel,
xmlDocPtr doc) xmlDocPtr doc)
{ {
xmlNodePtr cur; xmlNodePtr cur;
KatzeItem* item; KatzeItem* item;
cur = xmlDocGetRootElement (doc);
if ((cur = xmlDocGetRootElement (doc)) == NULL) if ((cur = xmlDocGetRootElement (doc)) == NULL)
{ {
/* Empty document */ /* Empty document */
@ -205,17 +221,17 @@ katze_array_from_xmlDocPtr (KatzeArray* array,
gchar* value; gchar* value;
value = (gchar*)xmlGetProp (cur, (xmlChar*)"version"); value = (gchar*)xmlGetProp (cur, (xmlChar*)"version");
if (!value || !katze_str_equal (value, "1.0")) if (!tiny_xbel && (!value || !katze_str_equal (value, "1.0")))
g_warning ("XBEL version is not 1.0."); g_warning ("XBEL version is not 1.0.");
g_free (value); xmlFree (value);
value = (gchar*)xmlGetProp (cur, (xmlChar*)"title"); value = (gchar*)xmlGetProp (cur, (xmlChar*)"title");
katze_item_set_name (KATZE_ITEM (array), value); katze_item_set_name (KATZE_ITEM (array), value);
g_free (value); xmlFree (value);
value = (gchar*)xmlGetProp (cur, (xmlChar*)"desc"); value = (gchar*)xmlGetProp (cur, (xmlChar*)"desc");
katze_item_set_text (KATZE_ITEM (array), value); katze_item_set_text (KATZE_ITEM (array), value);
g_free (value); xmlFree (value);
} }
else if (katze_str_equal ((gchar*)cur->name, "RDF")) else if (katze_str_equal ((gchar*)cur->name, "RDF"))
{ {
@ -227,21 +243,28 @@ katze_array_from_xmlDocPtr (KatzeArray* array,
if (katze_str_equal ((gchar*)cur->name, "item")) if (katze_str_equal ((gchar*)cur->name, "item"))
{ {
xmlNodePtr cur_item; xmlNodePtr cur_item;
item = katze_item_new (); item = katze_item_new ();
cur_item = cur->xmlChildrenNode; cur_item = cur->xmlChildrenNode;
while (cur_item) while (cur_item)
{ {
if (katze_str_equal ((gchar*)cur_item->name, "title")) if (katze_str_equal ((gchar*)cur_item->name, "title"))
item->name = g_strstrip ((gchar*)xmlNodeGetContent (cur_item)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur_item));
katze_item_set_name (item, value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur_item->name, "link")) else if (katze_str_equal ((gchar*)cur_item->name, "link"))
item->uri = g_strstrip ((gchar*)xmlNodeGetContent (cur_item)); {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur_item));
katze_item_set_uri (item, value);
xmlFree (value);
}
else if (katze_str_equal ((gchar*)cur_item->name, "subject")) else if (katze_str_equal ((gchar*)cur_item->name, "subject"))
{ {
gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur_item)); gchar* value = g_strstrip ((gchar*)xmlNodeGetContent (cur_item));
/* FIXME: Create a folder according to the tag */ /* FIXME: Create a folder according to the tag */
g_free (value); xmlFree (value);
} }
cur_item = cur_item->next; cur_item = cur_item->next;
} }
@ -378,7 +401,11 @@ katze_array_from_netscape_file (KatzeArray* array,
if (item && katze_str_equal (element[1], "DD")) if (item && katze_str_equal (element[1], "DD"))
{ {
if (element[2]) if (element[2])
item->text = katze_unescape_html (element[2]); {
gchar* text = katze_unescape_html (element[2]);
katze_item_set_text (item, text);
g_free (text);
}
item = NULL; item = NULL;
} }
/* end of current folder, level-up */ /* end of current folder, level-up */
@ -459,16 +486,16 @@ katze_array_from_opera_file (KatzeArray* array,
if (parts && parts[0] && parts[1]) if (parts && parts[0] && parts[1])
{ {
if (katze_str_equal (parts[0], "NAME")) if (katze_str_equal (parts[0], "NAME"))
item->name = g_strdup (parts[1]); katze_item_set_name (item, parts[1]);
else if (katze_str_equal (parts[0], "URL")) else if (katze_str_equal (parts[0], "URL"))
item->uri = g_strdup (parts[1]); katze_item_set_uri (item, parts[1]);
else if (katze_str_equal (parts[0], "DESCRIPTION")) else if (katze_str_equal (parts[0], "DESCRIPTION"))
item->text = g_strdup (parts[1]); katze_item_set_text (item, parts[1]);
else if (katze_str_equal (parts[0], "CREATED")) else if (katze_str_equal (parts[0], "CREATED"))
item->added = g_ascii_strtoull (parts[1], NULL, 10); katze_item_set_added (item, g_ascii_strtoull (parts[1], NULL, 10));
/* FIXME: Implement visited time /* FIXME: Implement visited time
else if (katze_str_equal (parts[0], "VISITED")) else if (katze_str_equal (parts[0], "VISITED"))
item->visited = g_ascii_strtoull (parts[1], NULL, 10); */ katze_item_set_visited (item, g_ascii_strtoull (parts[1], NULL, 10)); */
else if (katze_str_equal (parts[0], "ON PERSONALBAR")) else if (katze_str_equal (parts[0], "ON PERSONALBAR"))
katze_item_set_meta_integer (item, "toolbar", katze_item_set_meta_integer (item, "toolbar",
katze_str_equal (parts[1], "YES") ? 1 : -1); katze_str_equal (parts[1], "YES") ? 1 : -1);
@ -597,6 +624,7 @@ midori_array_from_file (KatzeArray* array,
/* XBEL */ /* XBEL */
if (katze_str_equal (format, "xbel") if (katze_str_equal (format, "xbel")
|| katze_str_equal (format, "xbel-tiny")
|| !*format) || !*format)
{ {
xmlDocPtr doc; xmlDocPtr doc;
@ -610,7 +638,7 @@ midori_array_from_file (KatzeArray* array,
return FALSE; return FALSE;
} }
if (!katze_array_from_xmlDocPtr (array, doc)) if (!katze_array_from_xmlDocPtr (array, katze_str_equal (format, "xbel-tiny"), doc))
{ {
/* Parsing failed */ /* Parsing failed */
xmlFreeDoc (doc); xmlFreeDoc (doc);
@ -701,13 +729,14 @@ string_append_xml_element (GString* string,
static void static void
string_append_item (GString* string, string_append_item (GString* string,
KatzeItem* item) KatzeItem* item,
gboolean tiny_xbel)
{ {
gchar* metadata; gchar* metadata;
g_return_if_fail (KATZE_IS_ITEM (item)); g_return_if_fail (KATZE_IS_ITEM (item));
metadata = katze_item_metadata_to_xbel (item); metadata = katze_item_metadata_to_xbel (item, tiny_xbel);
if (KATZE_IS_ARRAY (item)) if (KATZE_IS_ARRAY (item))
{ {
KatzeItem* _item; KatzeItem* _item;
@ -719,7 +748,7 @@ string_append_item (GString* string,
string_append_xml_element (string, "title", katze_item_get_name (item)); string_append_xml_element (string, "title", katze_item_get_name (item));
string_append_xml_element (string, "desc", katze_item_get_text (item)); string_append_xml_element (string, "desc", katze_item_get_text (item));
KATZE_ARRAY_FOREACH_ITEM_L (_item, array, list) KATZE_ARRAY_FOREACH_ITEM_L (_item, array, list)
string_append_item (string, _item); string_append_item (string, _item, tiny_xbel);
g_string_append (string, metadata); g_string_append (string, metadata);
g_string_append (string, "</folder>\n"); g_string_append (string, "</folder>\n");
g_list_free (list); g_list_free (list);
@ -729,7 +758,12 @@ string_append_item (GString* string,
g_string_append (string, "<bookmark href=\""); g_string_append (string, "<bookmark href=\"");
string_append_escaped (string, katze_item_get_uri (item)); string_append_escaped (string, katze_item_get_uri (item));
g_string_append (string, "\">\n"); g_string_append (string, "\">\n");
string_append_xml_element (string, "title", katze_item_get_name (item)); /* Strip LRE leading character */
if (item->name != NULL && g_str_has_prefix (item->name, ""))
string_append_xml_element (string, "title",
g_utf8_next_char (strstr (item->name, "")));
else
string_append_xml_element (string, "title", item->name);
string_append_xml_element (string, "desc", katze_item_get_text (item)); string_append_xml_element (string, "desc", katze_item_get_text (item));
g_string_append (string, metadata); g_string_append (string, metadata);
g_string_append (string, "</bookmark>\n"); g_string_append (string, "</bookmark>\n");
@ -772,7 +806,7 @@ string_append_netscape_item (GString* string,
string_append_escaped (string, katze_item_get_name (item)); string_append_escaped (string, katze_item_get_name (item));
g_string_append (string, "</A>\n"); g_string_append (string, "</A>\n");
if (item->text && g_strcmp0 (item->text, "")) if (g_strcmp0 (katze_str_non_null (katze_item_get_text (item)), ""))
{ {
g_string_append (string, "\t<DD>"); g_string_append (string, "\t<DD>");
string_append_escaped (string, katze_item_get_text (item)); string_append_escaped (string, katze_item_get_text (item));
@ -782,7 +816,8 @@ string_append_netscape_item (GString* string,
} }
static gchar* static gchar*
katze_item_metadata_to_xbel (KatzeItem* item) katze_item_metadata_to_xbel (KatzeItem* item,
gboolean tiny_xbel)
{ {
GList* keys = katze_item_get_meta_keys (item); GList* keys = katze_item_get_meta_keys (item);
GString* markup; GString* markup;
@ -812,7 +847,7 @@ katze_item_metadata_to_xbel (KatzeItem* item)
string_append_escaped (markdown, value); string_append_escaped (markdown, value);
g_string_append_printf (markdown, "</%s>\n", key); g_string_append_printf (markdown, "</%s>\n", key);
} }
else if (namespace) else if (namespace || tiny_xbel)
{ {
g_string_append_printf (markup, " %s=\"", key); g_string_append_printf (markup, " %s=\"", key);
string_append_escaped (markup, value); string_append_escaped (markup, value);
@ -825,7 +860,7 @@ katze_item_metadata_to_xbel (KatzeItem* item)
g_string_append_c (markup, '\"'); g_string_append_c (markup, '\"');
} }
} }
if (!namespace) if (!namespace && !tiny_xbel)
{ {
namespace_uri = "http://www.twotoasts.de"; namespace_uri = "http://www.twotoasts.de";
g_string_append_printf (markup, " owner=\"%s\"", namespace_uri); g_string_append_printf (markup, " owner=\"%s\"", namespace_uri);
@ -840,25 +875,27 @@ katze_item_metadata_to_xbel (KatzeItem* item)
static gchar* static gchar*
katze_array_to_xbel (KatzeArray* array, katze_array_to_xbel (KatzeArray* array,
gboolean tiny_xbel,
GError** error) GError** error)
{ {
gchar* metadata = katze_item_metadata_to_xbel (KATZE_ITEM (array)); gchar* metadata = katze_item_metadata_to_xbel (KATZE_ITEM (array), tiny_xbel);
KatzeItem* item; KatzeItem* item;
GList* list; GList* list;
GString* markup = g_string_new ( GString* markup = g_string_new ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" if (tiny_xbel)
g_string_append (markup, "<xbel>\n");
else
g_string_append (markup,
"<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD " "<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD "
"XML Bookmark Exchange Language 1.0//EN//XML\" " "XML Bookmark Exchange Language 1.0//EN//XML\" "
"\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n" "\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n"
"<xbel version=\"1.0\"" "<xbel version=\"1.0\" xmlns:midori=\"http://www.twotoasts.de\">\n");
" xmlns:midori=\"http://www.twotoasts.de\""
">\n");
string_append_xml_element (markup, "title", katze_item_get_name (KATZE_ITEM (array))); string_append_xml_element (markup, "title", katze_item_get_name (KATZE_ITEM (array)));
string_append_xml_element (markup, "desc", katze_item_get_text (KATZE_ITEM (array))); string_append_xml_element (markup, "desc", katze_item_get_text (KATZE_ITEM (array)));
g_string_append (markup, metadata ? metadata : ""); g_string_append (markup, metadata ? metadata : "");
KATZE_ARRAY_FOREACH_ITEM_L (item, array, list) KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
string_append_item (markup, item); string_append_item (markup, item, tiny_xbel);
g_string_append (markup, "</xbel>\n"); g_string_append (markup, "</xbel>\n");
g_free (metadata); g_free (metadata);
@ -901,24 +938,20 @@ midori_array_to_file_format (KatzeArray* array,
GError** error) GError** error)
{ {
gchar* data; gchar* data;
FILE* fp; gboolean success;
if (!g_strcmp0 (format, "xbel")) if (!g_strcmp0 (format, "xbel"))
data = katze_array_to_xbel (array, error); data = katze_array_to_xbel (array, FALSE, error);
else if (!g_strcmp0 (format, "xbel-tiny"))
data = katze_array_to_xbel (array, TRUE, error);
else if (!g_strcmp0 (format, "netscape")) else if (!g_strcmp0 (format, "netscape"))
data = katze_array_to_netscape_html (array, error); data = katze_array_to_netscape_html (array, error);
else else
return FALSE; return FALSE;
if (!(fp = fopen (filename, "w")))
{ success = g_file_set_contents (filename, data, -1, error);
*error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_ACCES,
_("Writing failed."));
return FALSE;
}
fputs (data, fp);
fclose (fp);
g_free (data); g_free (data);
return TRUE; return success;
} }
/** /**
@ -945,6 +978,7 @@ midori_array_to_file (KatzeArray* array,
g_return_val_if_fail (!error || !*error, FALSE); g_return_val_if_fail (!error || !*error, FALSE);
if (!g_strcmp0 (format, "xbel") if (!g_strcmp0 (format, "xbel")
|| !g_strcmp0 (format, "xbel-tiny")
|| !g_strcmp0 (format, "netscape")) || !g_strcmp0 (format, "netscape"))
return midori_array_to_file_format (array, filename, format, error); return midori_array_to_file_format (array, filename, format, error);
@ -967,41 +1001,44 @@ katze_item_set_value_from_column (sqlite3_stmt* stmt,
const unsigned char* uri; const unsigned char* uri;
uri = sqlite3_column_text (stmt, column); uri = sqlite3_column_text (stmt, column);
if (uri && uri[0] && uri[0] != '(') if (uri && uri[0] && uri[0] != '(')
item->uri = g_strdup ((gchar*)uri); katze_item_set_uri (item, (gchar*)uri);
} }
else if (g_str_equal (name, "title") || g_str_equal (name, "name")) else if (g_str_equal (name, "title") || g_str_equal (name, "name"))
{ {
const unsigned char* title; const unsigned char* title;
title = sqlite3_column_text (stmt, column); title = sqlite3_column_text (stmt, column);
item->name = g_strdup ((gchar*)title); katze_item_set_name (item, (gchar*)title);
} }
else if (g_str_equal (name, "date")) else if (g_str_equal (name, "date") || g_str_equal (name, "created"))
{ {
gint date; gint date;
date = sqlite3_column_int64 (stmt, column); date = sqlite3_column_int64 (stmt, column);
item->added = date; katze_item_set_added (item, date);
} }
else if (g_str_equal (name, "day") || g_str_equal (name, "app") else if (g_str_equal (name, "day") || g_str_equal (name, "app")
|| g_str_equal (name, "toolbar")) || g_str_equal (name, "toolbar") || g_str_equal (name, "id")
|| g_str_equal (name, "parentid") || g_str_equal (name, "seq")
|| g_str_equal (name, "last_visit") || g_str_equal (name, "visit_count")
|| g_str_equal (name, "pos_panel") || g_str_equal (name, "pos_bar"))
{ {
gint value; gint value;
value = sqlite3_column_int64 (stmt, column); value = sqlite3_column_int64 (stmt, column);
katze_item_set_meta_integer (item, name, value); katze_item_set_meta_integer (item, name, value);
} }
else if (g_str_equal (name, "folder"))
{
const unsigned char* folder;
folder = sqlite3_column_text (stmt, column);
katze_item_set_meta_string (item, name, (gchar*)folder);
}
else if (g_str_equal (name, "desc")) else if (g_str_equal (name, "desc"))
{ {
const unsigned char* text; const unsigned char* text;
text = sqlite3_column_text (stmt, column); text = sqlite3_column_text (stmt, column);
item->text = g_strdup ((gchar*)text); katze_item_set_text (item, (gchar*)text);
}
else if (g_str_equal (name, "nick"))
{
const unsigned char* sql;
sql = sqlite3_column_text (stmt, column);
katze_item_set_meta_string (item, name, (gchar*)sql);
} }
else else
g_warn_if_reached (); g_critical ("%s: Unexpected column '%s'", G_STRFUNC, name);
} }
/** /**
@ -1066,6 +1103,73 @@ katze_array_from_sqlite (sqlite3* db,
return katze_array_from_statement (stmt); return katze_array_from_statement (stmt);
} }
/**
* midori_array_query_recursive:
* @array: the main bookmark array
* @fields: comma separated list of fields
* @condition: condition, like "folder = '%q'"
* @value: a value to be inserted if @condition contains %q
* @recursive: if %TRUE include children
*
* Stores the result in a #KatzeArray.
*
* Return value: a #KatzeArray on success, %NULL otherwise
*
* Since: 0.4.4
**/
KatzeArray*
midori_array_query_recursive (KatzeArray* bookmarks,
const gchar* fields,
const gchar* condition,
const gchar* value,
gboolean recursive)
{
sqlite3* db;
gchar* sqlcmd;
char* sqlcmd_value;
KatzeArray* array;
KatzeItem* item;
GList* list;
g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), NULL);
g_return_val_if_fail (fields, NULL);
g_return_val_if_fail (condition, NULL);
db = g_object_get_data (G_OBJECT (bookmarks), "db");
g_return_val_if_fail (db != NULL, NULL);
sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
"ORDER BY (uri='') ASC, title DESC", fields, condition);
if (strstr (condition, "%q"))
{
sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
array = katze_array_from_sqlite (db, sqlcmd_value);
sqlite3_free (sqlcmd_value);
}
else
array = katze_array_from_sqlite (db, sqlcmd);
g_free (sqlcmd);
if (!recursive)
return array;
KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
{
if (KATZE_ITEM_IS_FOLDER (item))
{
gchar* parentid = g_strdup_printf ("%" G_GINT64_FORMAT,
katze_item_get_meta_integer (item, "id"));
KatzeArray* subarray = midori_array_query_recursive (bookmarks,
fields, "parentid=%q", parentid, TRUE);
katze_item_set_name (KATZE_ITEM (subarray), katze_item_get_name (item));
katze_array_add_item (array, subarray);
g_free (parentid);
}
}
g_list_free (list);
return array;
}
/** /**
* midori_array_query: * midori_array_query:
* @array: the main bookmark array * @array: the main bookmark array
@ -1078,6 +1182,8 @@ katze_array_from_sqlite (sqlite3* db,
* Return value: a #KatzeArray on success, %NULL otherwise * Return value: a #KatzeArray on success, %NULL otherwise
* *
* Since: 0.4.3 * Since: 0.4.3
*
* Deprecated: 0.4.4: Use midori_array_query_recursive() instead.
**/ **/
KatzeArray* KatzeArray*
midori_array_query (KatzeArray* bookmarks, midori_array_query (KatzeArray* bookmarks,
@ -1085,29 +1191,6 @@ midori_array_query (KatzeArray* bookmarks,
const gchar* condition, const gchar* condition,
const gchar* value) const gchar* value)
{ {
sqlite3* db; return midori_array_query_recursive (bookmarks, fields, condition, value, FALSE);
gchar* sqlcmd;
char* sqlcmd_value;
KatzeArray* array;
g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), NULL);
g_return_val_if_fail (fields, NULL);
g_return_val_if_fail (condition, NULL);
db = g_object_get_data (G_OBJECT (bookmarks), "db");
if (db == NULL)
return NULL;
sqlcmd = g_strdup_printf ("SELECT %s FROM bookmarks WHERE %s "
"ORDER BY title DESC", fields, condition);
if (strstr (condition, "%q"))
{
sqlcmd_value = sqlite3_mprintf (sqlcmd, value ? value : "");
array = katze_array_from_sqlite (db, sqlcmd_value);
sqlite3_free (sqlcmd_value);
}
else
array = katze_array_from_sqlite (db, sqlcmd);
g_free (sqlcmd);
return array;
} }

View file

@ -36,6 +36,13 @@ midori_array_query (KatzeArray* array,
const gchar* condition, const gchar* condition,
const gchar* value); const gchar* value);
KatzeArray*
midori_array_query_recursive (KatzeArray* array,
const gchar* fields,
const gchar* condition,
const gchar* value,
gboolean recursive);
KatzeArray* KatzeArray*
katze_array_from_sqlite (sqlite3* db, katze_array_from_sqlite (sqlite3* db,
const gchar* sqlcmd); const gchar* sqlcmd);

327
midori/midori-bookmarks.c Normal file
View file

@ -0,0 +1,327 @@
/*
Copyright (C) 2010 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2010 Alexander Butenko <a.butenka@gmail.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.
*/
#include "midori-bookmarks.h"
#include "panels/midori-bookmarks.h"
#include "midori-app.h"
#include "midori-array.h"
#include "sokoke.h"
#include "midori-core.h"
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
void
midori_bookmarks_dbtracer (void* dummy,
const char* query)
{
g_printerr ("%s\n", query);
}
void
midori_bookmarks_add_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
midori_bookmarks_insert_item_db (db, item,
katze_item_get_meta_integer (item, "parentid"));
}
void
midori_bookmarks_remove_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
char* errmsg = NULL;
sqlcmd = sqlite3_mprintf (
"DELETE FROM bookmarks WHERE id = %" G_GINT64_FORMAT ";",
katze_item_get_meta_integer (item, "id"));
if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
{
g_printerr (_("Failed to remove history item: %s\n"), errmsg);
sqlite3_free (errmsg);
}
sqlite3_free (sqlcmd);
}
#define _APPEND_TO_SQL_ERRORMSG(custom_errmsg) \
do { \
if (sql_errmsg) \
{ \
g_string_append_printf (errmsg_str, "%s : %s\n", custom_errmsg, sql_errmsg); \
sqlite3_free (sql_errmsg); \
} \
else \
g_string_append (errmsg_str, custom_errmsg); \
} while (0)
gboolean
midori_bookmarks_import_from_old_db (sqlite3* db,
const gchar* oldfile,
gchar** errmsg)
{
gint sql_errcode;
gboolean failure = FALSE;
gchar* sql_errmsg = NULL;
GString* errmsg_str = g_string_new (NULL);
gchar* attach_stmt = sqlite3_mprintf ("ATTACH DATABASE %Q AS old_db;", oldfile);
const gchar* convert_stmts =
"BEGIN TRANSACTION;"
"INSERT INTO main.bookmarks (parentid, title, uri, desc, app, toolbar) "
"SELECT NULL AS parentid, title, uri, desc, app, toolbar "
"FROM old_db.bookmarks;"
"UPDATE main.bookmarks SET parentid = ("
"SELECT id FROM main.bookmarks AS b1 WHERE b1.title = ("
"SELECT folder FROM old_db.bookmarks WHERE title = main.bookmarks.title));"
"COMMIT;";
const gchar* detach_stmt = "DETACH DATABASE old_db;";
*errmsg = NULL;
sql_errcode = sqlite3_exec (db, attach_stmt, NULL, NULL, &sql_errmsg);
sqlite3_free (attach_stmt);
if (sql_errcode != SQLITE_OK)
{
_APPEND_TO_SQL_ERRORMSG (_("failed to ATTACH old db"));
goto convert_failed;
}
if (sqlite3_exec (db, convert_stmts, NULL, NULL, &sql_errmsg) != SQLITE_OK)
{
failure = TRUE;
_APPEND_TO_SQL_ERRORMSG (_("failed to import from old db"));
/* try to get back to previous state */
if (sqlite3_exec (db, "ROLLBACK TRANSACTION;", NULL, NULL, &sql_errmsg) != SQLITE_OK)
_APPEND_TO_SQL_ERRORMSG (_("failed to rollback the transaction"));
}
if (sqlite3_exec (db, detach_stmt, NULL, NULL, &sql_errmsg) != SQLITE_OK)
_APPEND_TO_SQL_ERRORMSG (_("failed to DETACH "));
if (failure)
{
convert_failed:
*errmsg = g_string_free (errmsg_str, FALSE);
g_print ("ERRORR: %s\n", errmsg_str->str);
return FALSE;
}
return TRUE;
}
#undef _APPEND_TO_SQL_ERRORMSG
KatzeArray*
midori_bookmarks_new (char** errmsg)
{
sqlite3* db;
gchar* oldfile;
gchar* newfile;
gboolean newfile_did_exist, oldfile_exists;
const gchar* create_stmt;
gchar* sql_errmsg = NULL;
gchar* import_errmsg = NULL;
KatzeArray* array;
g_return_val_if_fail (errmsg != NULL, NULL);
oldfile = midori_paths_get_config_filename_for_writing ("bookmarks.db");
oldfile_exists = g_access (oldfile, F_OK) == 0;
newfile = midori_paths_get_config_filename_for_writing ("bookmarks_v2.db");
newfile_did_exist = g_access (newfile, F_OK) == 0;
/* sqlite3_open will create the file if it did not exists already */
if (sqlite3_open (newfile, &db) != SQLITE_OK)
{
*errmsg = g_strdup_printf (_("Failed to open database: %s\n"),
db ? sqlite3_errmsg (db) : "(db = NULL)");
goto init_failed;
}
if (midori_debug ("bookmarks"))
sqlite3_trace (db, midori_bookmarks_dbtracer, NULL);
create_stmt = /* Table structure */
"CREATE TABLE IF NOT EXISTS bookmarks "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, "
"parentid INTEGER DEFAULT NULL, "
"title TEXT, uri TEXT, desc TEXT, app INTEGER, toolbar INTEGER, "
"pos_panel INTEGER, pos_bar INTEGER, "
"created DATE DEFAULT CURRENT_TIMESTAMP, "
"last_visit DATE, visit_count INTEGER DEFAULT 0, "
"nick TEXT, "
"FOREIGN KEY(parentid) REFERENCES bookmarks(id) "
"ON DELETE CASCADE); PRAGMA foreign_keys = ON;"
/* trigger: insert panel position */
"CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosPanel "
"AFTER INSERT ON bookmarks FOR EACH ROW "
"BEGIN UPDATE bookmarks SET pos_panel = ("
"SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks WHERE "
"(NEW.parentid IS NOT NULL AND parentid = NEW.parentid) "
"OR (NEW.parentid IS NULL AND parentid IS NULL)) "
"WHERE id = NEW.id; END;"
/* trigger: insert Bookmarkbar position */
"CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosBar "
"AFTER INSERT ON bookmarks FOR EACH ROW WHEN NEW.toolbar=1 "
"BEGIN UPDATE bookmarks SET pos_bar = ("
"SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE "
"((NEW.parentid IS NOT NULL AND parentid = NEW.parentid) "
"OR (NEW.parentid IS NULL AND parentid IS NULL)) AND toolbar=1) "
"WHERE id = NEW.id; END;"
/* trigger: update panel position */
"CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosPanel "
"BEFORE UPDATE OF parentid ON bookmarks FOR EACH ROW "
"WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) "
"AND NEW.parentid IS NOT OLD.parentid) OR "
"((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) "
"AND NEW.parentid!=OLD.parentid) "
"BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1 "
"WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) "
"OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel; "
"UPDATE bookmarks SET pos_panel = ("
"SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks "
"WHERE (NEW.parentid IS NOT NULL AND parentid = NEW.parentid) "
"OR (NEW.parentid IS NULL AND parentid IS NULL)) "
"WHERE id = OLD.id; END;"
/* trigger: update Bookmarkbar position */
"CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar0 "
"AFTER UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW "
"WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) "
"AND NEW.parentid IS NOT OLD.parentid) "
"OR ((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) "
"AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=1 AND NEW.toolbar=0) "
"BEGIN UPDATE bookmarks SET pos_bar = NULL WHERE id = NEW.id; "
"UPDATE bookmarks SET pos_bar = pos_bar-1 "
"WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) "
"OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;"
/* trigger: update Bookmarkbar position */
"CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar1 "
"BEFORE UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW "
"WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL) "
"AND NEW.parentid IS NOT OLD.parentid) OR "
"((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL) "
"AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=0 AND NEW.toolbar=1) "
"BEGIN UPDATE bookmarks SET pos_bar = ("
"SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE "
"(NEW.parentid IS NOT NULL AND parentid = NEW.parentid) "
"OR (NEW.parentid IS NULL AND parentid IS NULL)) "
"WHERE id = OLD.id; END;"
/* trigger: delete panel position */
"CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosPanel "
"AFTER DELETE ON bookmarks FOR EACH ROW "
"BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1 "
"WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) "
"OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel; END;"
/* trigger: delete Bookmarkbar position */
"CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosBar "
"AFTER DELETE ON bookmarks FOR EACH ROW WHEN OLD.toolbar=1 "
"BEGIN UPDATE bookmarks SET pos_bar = pos_bar-1 "
"WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid) "
"OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;";
if (newfile_did_exist)
{
/* we are done */
goto init_success;
}
else
{
/* initial creation */
if (sqlite3_exec (db, create_stmt, NULL, NULL, &sql_errmsg) != SQLITE_OK)
{
*errmsg = g_strdup_printf (_("Couldn't create bookmarks table: %s\n"),
sql_errmsg ? sql_errmsg : "(err = NULL)");
sqlite3_free (sql_errmsg);
/* we can as well remove the new file */
g_unlink (newfile);
goto init_failed;
}
}
if (oldfile_exists)
/* import from old db */
if (!midori_bookmarks_import_from_old_db (db, oldfile, &import_errmsg))
{
*errmsg = g_strdup_printf (_("Couldn't import from old database: %s\n"),
import_errmsg ? import_errmsg : "(err = NULL)");
g_free (import_errmsg);
}
init_success:
g_free (newfile);
g_free (oldfile);
array = katze_array_new (KATZE_TYPE_ARRAY);
g_signal_connect (array, "add-item",
G_CALLBACK (midori_bookmarks_add_item_cb), db);
g_signal_connect (array, "remove-item",
G_CALLBACK (midori_bookmarks_remove_item_cb), db);
g_object_set_data (G_OBJECT (array), "db", db);
return array;
init_failed:
g_free (newfile);
g_free (oldfile);
if (db)
sqlite3_close (db);
return NULL;
}
void
midori_bookmarks_import (const gchar* filename,
sqlite3* db)
{
KatzeArray* bookmarks;
GError* error = NULL;
bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
if (!midori_array_from_file (bookmarks, filename, "xbel", &error))
{
g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
g_error_free (error);
return;
}
midori_bookmarks_import_array_db (db, bookmarks, 0);
}
void
midori_bookmarks_on_quit (KatzeArray* array)
{
g_return_if_fail (KATZE_IS_ARRAY (array));
sqlite3* db = g_object_get_data (G_OBJECT (array), "db");
g_return_if_fail (db != NULL);
sqlite3_close (db);
}

40
midori/midori-bookmarks.h Normal file
View file

@ -0,0 +1,40 @@
/*
Copyright (C) 2010 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2010 Alexander Butenko <a.butenka@gmail.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.
*/
#ifndef __MIDORI_BOOKMARKS_H__
#define __MIDORI_BOOKMARKS_H__ 1
#include <sqlite3.h>
#include <katze/katze.h>
void
midori_bookmarks_add_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db);
void
midori_bookmarks_remove_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db);
KatzeArray*
midori_bookmarks_new (char** errmsg);
void
midori_bookmarks_on_quit (KatzeArray* array);
void
midori_bookmarks_import (const gchar* filename,
sqlite3* db);
#endif /* !__MIDORI_BOOKMARKS_H__ */

File diff suppressed because it is too large Load diff

View file

@ -12,11 +12,6 @@
#ifndef __MIDORI_BROWSER_H__ #ifndef __MIDORI_BROWSER_H__
#define __MIDORI_BROWSER_H__ #define __MIDORI_BROWSER_H__
#include <webkit/webkit.h>
#if defined(HAVE_HILDON) && HAVE_HILDON
#include <hildon/hildon.h>
#endif
#include <katze/katze.h> #include <katze/katze.h>
#include "midori-view.h" #include "midori-view.h"
@ -40,16 +35,16 @@ typedef struct _MidoriBrowserClass MidoriBrowserClass;
struct _MidoriBrowserClass struct _MidoriBrowserClass
{ {
#if defined(HAVE_HILDON) && HAVE_HILDON
HildonWindowClass parent_class;
#else
GtkWindowClass parent_class; GtkWindowClass parent_class;
#endif
/* Signals */ /* Signals */
void void
(*window_object_cleared) (MidoriBrowser* browser, (*window_object_cleared) (MidoriBrowser* browser,
#ifndef HAVE_WEBKIT2
WebKitWebFrame* web_frame, WebKitWebFrame* web_frame,
#else
void* web_frame,
#endif
JSContextRef* context, JSContextRef* context,
JSObjectRef* window_object); JSObjectRef* window_object);
void void
@ -81,24 +76,19 @@ midori_browser_get_type (void) G_GNUC_CONST;
MidoriBrowser* MidoriBrowser*
midori_browser_new (void); midori_browser_new (void);
gint void
midori_browser_add_tab (MidoriBrowser* browser, midori_browser_add_tab (MidoriBrowser* browser,
GtkWidget* widget); GtkWidget* widget);
void void
midori_browser_remove_tab (MidoriBrowser* browser, midori_browser_close_tab (MidoriBrowser* browser,
GtkWidget* widget); GtkWidget* widget);
void GtkWidget*
midori_browser_foreach (MidoriBrowser* browser,
GtkCallback callback,
gpointer callback_data);
gint
midori_browser_add_item (MidoriBrowser* browser, midori_browser_add_item (MidoriBrowser* browser,
KatzeItem* item); KatzeItem* item);
gint GtkWidget*
midori_browser_add_uri (MidoriBrowser* browser, midori_browser_add_uri (MidoriBrowser* browser,
const gchar* uri); const gchar* uri);
@ -106,6 +96,10 @@ void
midori_browser_activate_action (MidoriBrowser* browser, midori_browser_activate_action (MidoriBrowser* browser,
const gchar* name); const gchar* name);
void
midori_browser_assert_action (MidoriBrowser* browser,
const gchar* name);
void void
midori_browser_block_action (MidoriBrowser* browser, midori_browser_block_action (MidoriBrowser* browser,
GtkAction* action); GtkAction* action);
@ -132,6 +126,11 @@ midori_browser_get_current_uri (MidoriBrowser* browser);
void void
midori_browser_set_current_page_smartly (MidoriBrowser* browser, midori_browser_set_current_page_smartly (MidoriBrowser* browser,
gint n); gint n);
void
midori_browser_set_current_tab_smartly (MidoriBrowser* browser,
GtkWidget* view);
void void
midori_browser_set_current_page (MidoriBrowser* browser, midori_browser_set_current_page (MidoriBrowser* browser,
gint n); gint n);
@ -139,6 +138,10 @@ midori_browser_set_current_page (MidoriBrowser* browser,
gint gint
midori_browser_get_current_page (MidoriBrowser* browser); midori_browser_get_current_page (MidoriBrowser* browser);
void
midori_browser_set_current_item (MidoriBrowser* browser,
KatzeItem* item);
GtkWidget* GtkWidget*
midori_browser_get_nth_tab (MidoriBrowser* browser, midori_browser_get_nth_tab (MidoriBrowser* browser,
gint n); gint n);
@ -152,11 +155,15 @@ GtkWidget*
midori_browser_get_current_tab (MidoriBrowser* browser); midori_browser_get_current_tab (MidoriBrowser* browser);
#define midori_browser_get_tab midori_browser_get_current_tab #define midori_browser_get_tab midori_browser_get_current_tab
gint
midori_browser_page_num (MidoriBrowser* browser,
GtkWidget* view);
GList* GList*
midori_browser_get_tabs (MidoriBrowser* browser); midori_browser_get_tabs (MidoriBrowser* browser);
KatzeArray* gint
midori_browser_get_proxy_items (MidoriBrowser* browser); midori_browser_get_n_pages (MidoriBrowser* browser);
KatzeArray* KatzeArray*
midori_browser_get_proxy_array (MidoriBrowser* browser); midori_browser_get_proxy_array (MidoriBrowser* browser);
@ -173,6 +180,20 @@ midori_browser_get_toolbar_actions (MidoriBrowser* browser);
MidoriWebSettings* MidoriWebSettings*
midori_browser_get_settings (MidoriBrowser* browser); midori_browser_get_settings (MidoriBrowser* browser);
void
midori_browser_update_history (KatzeItem* item,
const gchar* type,
const gchar* event);
void
midori_browser_save_uri (MidoriBrowser* browser,
MidoriView* view,
const gchar* uri);
void
midori_browser_set_inactivity_reset (MidoriBrowser* browser,
gint inactivity_reset);
G_END_DECLS G_END_DECLS
#endif /* __MIDORI_BROWSER_H__ */ #endif /* __MIDORI_BROWSER_H__ */

View file

@ -0,0 +1,170 @@
/*
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
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 class Suggestion : GLib.Object {
public string? uri { get; set; }
public string? markup { get; set; }
public bool use_markup { get; set; }
public string? background { get; set; }
public GLib.Icon? icon { get; set; }
public bool action { get; set; default = false; }
public Suggestion (string? uri, string? markup, bool use_markup=false,
string? background=null, GLib.Icon? icon=null) {
GLib.Object (uri: uri, markup: markup, use_markup: use_markup,
background: background, icon: icon);
}
}
public abstract class Completion : GLib.Object {
public string? description { get; set; }
public int max_items { get; internal set; default = 25; }
internal int position { get; set; }
public abstract void prepare (GLib.Object app);
public abstract bool can_complete (string prefix);
public abstract bool can_action (string action);
public abstract async List<Suggestion>? complete (string text, string? action, Cancellable cancellable);
}
public class Autocompleter : GLib.Object {
private GLib.Object app;
private List<Completion> completions;
private int next_position;
public Gtk.ListStore model { get; private set; }
private bool need_to_clear = false;
private uint current_count = 0;
private Cancellable? cancellable = null;
public enum Columns {
ICON,
URI,
MARKUP,
BACKGROUND,
YALIGN,
N
}
public Autocompleter (GLib.Object app) {
this.app = app;
completions = new List<Completion> ();
next_position = 0;
model = new Gtk.ListStore (Columns.N,
typeof (Gdk.Pixbuf), typeof (string), typeof (string),
typeof (string), typeof (float));
}
public void add (Completion completion) {
completion.prepare (app);
completion.position = next_position;
next_position += completion.max_items;
completions.append (completion);
}
public bool can_complete (string text) {
foreach (var completion in completions)
if (completion.can_complete (text))
return true;
return false;
}
private void fill_model (Midori.Completion completion, List<Midori.Suggestion>? suggestions) {
if (need_to_clear) {
model.clear ();
need_to_clear = false;
current_count = 0;
}
#if HAVE_GRANITE
if (completion.description != null) {
model.insert_with_values (null, completion.position,
Columns.URI, "about:completion-description",
Columns.MARKUP, "<b>%s</b>\n".printf (Markup.escape_text (completion.description)),
Columns.ICON, null,
Columns.BACKGROUND, null,
Columns.YALIGN, 0.25);
}
#endif
int count = 1;
foreach (var suggestion in suggestions) {
if (suggestion.uri == null) {
warning ("suggestion.uri != null");
continue;
}
if (suggestion.markup == null) {
warning ("suggestion.markup != null");
continue;
}
model.insert_with_values (null, completion.position + count,
Columns.URI, suggestion.uri,
Columns.MARKUP, suggestion.use_markup
? suggestion.markup : Markup.escape_text (suggestion.markup),
Columns.ICON, suggestion.icon,
Columns.BACKGROUND, suggestion.background,
Columns.YALIGN, 0.25);
count++;
if (count > completion.max_items)
break;
}
current_count += count;
populated (current_count);
}
public signal void populated (uint count);
private async void complete_wrapped (Completion completion, string text, string? action, Cancellable cancellable) {
List<Midori.Suggestion>? suggestions = yield completion.complete (text, action, cancellable);
if (!cancellable.is_cancelled () && suggestions != null)
fill_model (completion, suggestions);
}
public async void complete (string text) {
if (cancellable != null)
cancellable.cancel ();
cancellable = new Cancellable ();
need_to_clear = true;
foreach (var completion in completions) {
if (completion.can_complete (text))
complete_wrapped.begin (completion, text, null, cancellable);
}
}
public bool can_action (string action) {
if (action == "about:completion-description")
return true;
foreach (var completion in completions)
if (completion.can_action (action))
return true;
return false;
}
public async void action (string action, string text) {
if (action == "about:completion-description")
return;
if (cancellable != null)
cancellable.cancel ();
cancellable = new Cancellable ();
need_to_clear = true;
foreach (var completion in completions) {
if (completion.can_action (action))
complete_wrapped.begin (completion, text, action, cancellable);
}
}
}
}

102
midori/midori-dialog.vala Normal file
View file

@ -0,0 +1,102 @@
/*
Copyright (C) 2011-2012 Christian Dywan <christian@twotoats.de>
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 {
namespace Timeout {
public uint add_seconds (uint interval, owned SourceFunc function) {
if (Test.test_idle_timeouts)
return GLib.Idle.add (function);
return GLib.Timeout.add_seconds (interval, function);
}
public uint add (uint interval, owned SourceFunc function) {
if (Test.test_idle_timeouts)
return GLib.Idle.add (function);
return GLib.Timeout.add (interval, function);
}
}
namespace Test {
internal static uint test_max_timeout = 0;
internal static string? test_first_try = null;
public void grab_max_timeout () {
int seconds = (Environment.get_variable ("MIDORI_TIMEOUT") ?? "42").to_int ();
test_first_try = "once";
test_max_timeout = GLib.Timeout.add_seconds (seconds > 0 ? seconds / 2 : 0, ()=>{
stdout.printf ("Timed out %s%s\n", test_first_try,
MainContext.default ().pending () ? " (loop)" : "");
if (test_first_try == "twice")
Process.exit (0);
test_first_try = "twice";
MainContext.default ().wakeup ();
return true;
});
}
public void release_max_timeout () {
assert (test_max_timeout > 0);
GLib.Source.remove (test_max_timeout);
test_max_timeout = 0;
}
internal static bool test_idle_timeouts = false;
public void idle_timeouts () {
test_idle_timeouts = true;
}
public void log_set_fatal_handler_for_icons () {
GLib.Test.log_set_fatal_handler ((domain, log_levels, message)=> {
return !message.contains ("Error loading theme icon")
&& !message.contains ("Could not find the icon")
&& !message.contains ("Junk at end of value")
&& !message.contains ("gtk_notebook_get_tab_label: assertion `GTK_IS_WIDGET (child)' failed")
&& !message.contains ("get_column_number: assertion `i < gtk_tree_view_get_n_columns (treeview)' failed");
});
}
internal static Gtk.ResponseType test_response = Gtk.ResponseType.NONE;
public void set_dialog_response (Gtk.ResponseType response) {
test_response = response;
}
internal static string? test_filename = null;
public void set_file_chooser_filename (string filename) {
test_filename = filename;
}
}
public class FileChooserDialog : Gtk.FileChooserDialog {
public FileChooserDialog (string title, Gtk.Window window, Gtk.FileChooserAction action) {
/* Creates a new file chooser dialog to Open or Save and Cancel.
The positive response is %Gtk.ResponseType.OK. */
unowned string stock_id = Gtk.Stock.OPEN;
if (action == Gtk.FileChooserAction.SAVE)
stock_id = Gtk.Stock.SAVE;
this.title = title;
transient_for = window;
this.action = action;
add_buttons (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
stock_id, Gtk.ResponseType.OK);
icon_name = stock_id;
}
}
namespace Dialog {
public static new int run (Gtk.Dialog dialog) {
if (Test.test_response != Gtk.ResponseType.NONE) {
if (Test.test_filename != null && dialog is Gtk.FileChooser)
(dialog as Gtk.FileChooser).set_filename (Test.test_filename);
return Test.test_response;
}
return dialog.run ();
}
}
}

364
midori/midori-download.vala Normal file
View file

@ -0,0 +1,364 @@
/*
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
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 Sokoke {
extern static bool show_uri (Gdk.Screen screen, string uri, uint32 timestamp) throws Error;
extern static bool message_dialog (Gtk.MessageType type, string short, string detailed, bool modal);
}
namespace Midori {
namespace Download {
public static bool is_finished (WebKit.Download download) {
#if !HAVE_WEBKIT2
switch (download.status) {
case WebKit.DownloadStatus.FINISHED:
case WebKit.DownloadStatus.CANCELLED:
case WebKit.DownloadStatus.ERROR:
return true;
default:
return false;
}
#else
return false;
#endif
}
public static int get_type (WebKit.Download download) {
return download.get_data<int> ("midori-download-type");
}
public static void set_type (WebKit.Download download, int type) {
download.set_data<int> ("midori-download-type", type);
}
public static double get_progress (WebKit.Download download) {
#if !HAVE_WEBKIT2
/* Avoid a bug in WebKit */
if (download.status == WebKit.DownloadStatus.CREATED)
return 0.0;
return download.progress;
#else
return 0.0;
#endif
}
#if !HAVE_GLIB_2_30
private static string format_size (uint64 size) {
return format_size_for_display ((int64)size);
}
#endif
public static string get_tooltip (WebKit.Download download) {
#if !HAVE_WEBKIT2
string filename = Path.get_basename (download.destination_uri);
/* i18n: Download tooltip (size): 4KB of 43MB */
string size = _("%s of %s").printf (
format_size (download.current_size),
format_size (download.total_size));
/* Finished, no speed or remaining time */
if (is_finished (download) || download.status == WebKit.DownloadStatus.CREATED)
return "%s\n%s".printf (filename, size);
uint64 total_size = download.total_size, current_size = download.current_size;
double elapsed = download.get_elapsed_time (),
diff = elapsed / current_size,
estimated = (total_size - current_size) * diff;
int hour = 3600, minute = 60;
int hours = (int)(estimated / hour),
minutes = (int)((estimated - (hours * hour)) / minute),
seconds = (int)((estimated - (hours * hour) - (minutes * minute)));
string hours_ = ngettext ("%d hour", "%d hours", hours).printf (hours);
string minutes_ = ngettext ("%d minute", "%d minutes", minutes).printf (minutes);
string seconds_ = ngettext ("%d second", "%d seconds", seconds).printf (seconds);
double last_time = download.get_data<int> ("last-time");
string eta = "";
if (estimated > 0) {
if (hours > 0)
eta = hours_ + ", " + minutes_;
else if (minutes >= 10)
eta = minutes_;
else if (minutes < 10 && minutes > 0)
eta = minutes_ + ", " + seconds_;
else if (seconds > 0)
eta = seconds_;
if (eta != "")
/* i18n: Download tooltip (estimated time) : - 1 hour, 5 minutes remaning */
eta = _(" - %s remaining").printf (eta);
}
string speed = "";
uint64? last_size = download.get_data<uint64?> ("last-size");
if (last_size != null && elapsed != last_time) {
speed = format_size ((uint64)(
(current_size - last_size) / (elapsed - last_time)));
}
else
/* i18n: Unknown number of bytes, used for transfer rate like ?B/s */
speed = _("?B");
/* i18n: Download tooltip (transfer rate): (130KB/s) */
speed = _(" (%s/s)").printf (speed);
if (elapsed - last_time > 0.0) {
download.set_data<int> ("last-time", (int)elapsed);
download.set_data<uint64?> ("last-size", current_size);
}
return "%s\n%s %s%s".printf (filename, size, speed, eta);
#else
return "";
#endif
}
public static string get_content_type (WebKit.Download download, string? mime_type) {
#if !HAVE_WEBKIT2
string? content_type = ContentType.guess (download.suggested_filename, null, null);
if (content_type == null) {
content_type = ContentType.from_mime_type (mime_type);
if (content_type == null)
content_type = ContentType.from_mime_type ("application/octet-stream");
}
return content_type;
#else
return ContentType.from_mime_type ("application/octet-stream");
#endif
}
public static bool has_wrong_checksum (WebKit.Download download) {
#if !HAVE_WEBKIT2
int status = download.get_data<int> ("checksum-status");
if (status == 0) {
/* Link Fingerprint */
string? original_uri = download.network_request.get_data<string> ("midori-original-uri");
if (original_uri == null)
original_uri = download.get_uri ();
string? fingerprint;
ChecksumType checksum_type = URI.get_fingerprint (original_uri, out fingerprint, null);
/* By default, no wrong checksum */
status = 2;
if (fingerprint != null) {
try {
string filename = Filename.from_uri (download.destination_uri);
string contents;
size_t length;
bool y = FileUtils.get_contents (filename, out contents, out length);
string checksum = Checksum.compute_for_string (checksum_type, contents, length);
/* Checksums are case-insensitive */
if (!y || fingerprint.ascii_casecmp (checksum) != 0)
status = 1; /* wrong checksum */
}
catch (Error error) {
status = 1; /* wrong checksum */
}
}
download.set_data<int> ("checksum-status", status);
}
return status == 1;
#else
return false;
#endif
}
public static bool action_clear (WebKit.Download download, Gtk.Widget widget) throws Error {
#if !HAVE_WEBKIT2
switch (download.status) {
case WebKit.DownloadStatus.CREATED:
case WebKit.DownloadStatus.STARTED:
download.cancel ();
break;
case WebKit.DownloadStatus.FINISHED:
if (open (download, widget))
return true;
break;
case WebKit.DownloadStatus.CANCELLED:
return true;
default:
critical ("action_clear: %d", download.status);
warn_if_reached ();
break;
}
#endif
return false;
}
public static string action_stock_id (WebKit.Download download) {
#if !HAVE_WEBKIT2
switch (download.status) {
case WebKit.DownloadStatus.CREATED:
case WebKit.DownloadStatus.STARTED:
return Gtk.Stock.CANCEL;
case WebKit.DownloadStatus.FINISHED:
if (has_wrong_checksum (download))
return Gtk.Stock.DIALOG_WARNING;
return Gtk.Stock.OPEN;
case WebKit.DownloadStatus.CANCELLED:
return Gtk.Stock.CLEAR;
case WebKit.DownloadStatus.ERROR:
return Gtk.Stock.DIALOG_ERROR;
default:
critical ("action_stock_id: %d", download.status);
warn_if_reached ();
return Gtk.Stock.MISSING_IMAGE;
}
#else
return Gtk.Stock.MISSING_IMAGE;
#endif
}
public static bool open (WebKit.Download download, Gtk.Widget widget) throws Error {
#if !HAVE_WEBKIT2
if (!has_wrong_checksum (download))
return Sokoke.show_uri (widget.get_screen (),
download.destination_uri, Gtk.get_current_event_time ());
Sokoke.message_dialog (Gtk.MessageType.WARNING,
_("The downloaded file is erroneous."),
_("The checksum provided with the link did not match. This means the file is probably incomplete or was modified afterwards."),
true);
#endif
return false;
}
public unowned string fallback_extension (string? extension, string mime_type) {
if (extension != null && extension[0] != '\0')
return extension;
if ("css" in mime_type)
return ".css";
if ("javascript" in mime_type)
return ".js";
if ("html" in mime_type)
return ".htm";
if ("plain" in mime_type)
return ".txt";
return "";
}
public string clean_filename (string filename) {
#if HAVE_WIN32
return filename.delimit ("/\\<>:\"|?*", '_');
#else
return filename.delimit ("/", '_');
#endif
}
public string get_suggested_filename (WebKit.Download download) {
#if !HAVE_WEBKIT2
/* https://bugs.webkit.org/show_bug.cgi?id=83161
https://d19vezwu8eufl6.cloudfront.net/nlp/slides%2F03-01-FormalizingNB.pdf */
return clean_filename (download.get_suggested_filename ());
#else
return "";
#endif
}
public string get_filename_suggestion_for_uri (string mime_type, string uri) {
return_val_if_fail (Midori.URI.is_location (uri), uri);
string filename = File.new_for_uri (uri).get_basename ();
if (uri.index_of_char ('.') == -1)
return Path.build_filename (filename, fallback_extension (null, mime_type));
return filename;
}
public static string? get_extension_for_uri (string uri, out string basename = null) {
if (&basename != null)
basename = null;
/* Find the last slash and the last period *after* the last slash. */
int last_slash = uri.last_index_of_char ('/');
/* Huh, URI without slashes? */
if (last_slash == -1)
return null;
int period = uri.last_index_of_char ('.', last_slash);
if (period == -1)
return null;
/* Exclude the query: ?query=foobar */
int query = uri.last_index_of_char ('?', period);
/* The extension, or "." if it ended with a period */
string extension = uri.substring (period, query - period);
if (&basename != null)
basename = uri.substring (0, period);
return extension;
}
public string get_unique_filename (string filename) {
if (Posix.access (filename, Posix.F_OK) == 0) {
string basename;
string? extension = get_extension_for_uri (filename, out basename);
string? new_filename = null;
int i = 0;
do {
new_filename = "%s-%d%s".printf (basename, i++, extension ?? "");
} while (Posix.access (new_filename, Posix.F_OK) == 0);
return new_filename;
}
return filename;
}
public string prepare_destination_uri (WebKit.Download download, string? folder) {
string suggested_filename = get_suggested_filename (download);
string basename = File.new_for_uri (suggested_filename).get_basename ();
string download_dir;
if (folder == null) {
download_dir = Paths.get_tmp_dir ();
Katze.mkdir_with_parents (download_dir, 0700);
}
else
download_dir = folder;
string destination_filename = Path.build_filename (download_dir, basename);
try {
return Filename.to_uri (get_unique_filename (destination_filename));
}
catch (Error error) {
return "file://" + destination_filename;
}
}
public static bool has_enough_space (WebKit.Download download, string uri) {
#if !HAVE_WEBKIT2
var folder = File.new_for_uri (uri).get_parent ();
bool can_write;
uint64 free_space;
try {
var info = folder.query_filesystem_info ("filesystem::free");
free_space = info.get_attribute_uint64 ("filesystem::free");
info = folder.query_info ("access::can-write", 0);
can_write = info.get_attribute_boolean ("access::can-write");
}
catch (Error error) {
can_write = false;
free_space = 0;
}
if (free_space < download.total_size || !can_write) {
string message;
string detailed_message;
if (!can_write) {
message = _("The file \"%s\" can't be saved in this folder.").printf (
Path.get_basename (uri));
detailed_message = _("You don't have permission to write in this location.");
}
else if (free_space < download.total_size) {
message = _("There is not enough free space to download \"%s\".").printf (
Path.get_basename (uri));
detailed_message = _("The file needs %s but only %s are left.").printf (
format_size (download.total_size), format_size (free_space));
}
else
assert_not_reached ();
Sokoke.message_dialog (Gtk.MessageType.ERROR, message, detailed_message, false);
return false;
}
#endif
return true;
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2008-2009 Christian Dywan <christian@twotoasts.de> Copyright (C) 2008-2012 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net> Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -14,17 +14,21 @@
#include <katze/katze.h> #include <katze/katze.h>
#include "midori-platform.h" #include "midori-platform.h"
#include "midori-core.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>
G_DEFINE_TYPE (MidoriExtension, midori_extension, G_TYPE_OBJECT); G_DEFINE_TYPE (MidoriExtension, midori_extension, G_TYPE_OBJECT);
struct _MidoriExtensionPrivate struct _MidoriExtensionPrivate
{ {
gchar* stock_id;
gchar* name; gchar* name;
gchar* description; gchar* description;
gboolean use_markup;
gchar* version; gchar* version;
gchar* authors; gchar* authors;
gchar* website; gchar* website;
gchar* key;
MidoriApp* app; MidoriApp* app;
gint active; gint active;
@ -123,11 +127,14 @@ enum
{ {
PROP_0, PROP_0,
PROP_STOCK_ID,
PROP_NAME, PROP_NAME,
PROP_DESCRIPTION, PROP_DESCRIPTION,
PROP_USE_MARKUP,
PROP_VERSION, PROP_VERSION,
PROP_AUTHORS, PROP_AUTHORS,
PROP_WEBSITE PROP_WEBSITE,
PROP_KEY
}; };
enum { enum {
@ -208,6 +215,15 @@ midori_extension_class_init (MidoriExtensionClass* class)
flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS; flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS;
g_object_class_install_property (gobject_class,
PROP_STOCK_ID,
g_param_spec_string (
"stock-id",
"Stock ID",
"An optional icon stock ID",
NULL,
flags));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_NAME, PROP_NAME,
g_param_spec_string ( g_param_spec_string (
@ -226,6 +242,15 @@ midori_extension_class_init (MidoriExtensionClass* class)
NULL, NULL,
flags)); flags));
g_object_class_install_property (gobject_class,
PROP_USE_MARKUP,
g_param_spec_boolean (
"use-markup",
"Use Markup",
"Whether to use Pango markup",
FALSE,
flags));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_VERSION, PROP_VERSION,
g_param_spec_string ( g_param_spec_string (
@ -260,6 +285,23 @@ midori_extension_class_init (MidoriExtensionClass* class)
NULL, NULL,
flags)); flags));
/**
* MidoriExtension:key:
*
* The extension key.
* Needed if there is more than one extension object in a single module.
*
* Since: 0.4.5
*/
g_object_class_install_property (gobject_class,
PROP_KEY,
g_param_spec_string (
"key",
"Key",
"The extension key",
NULL,
flags));
g_type_class_add_private (class, sizeof (MidoriExtensionPrivate)); g_type_class_add_private (class, sizeof (MidoriExtensionPrivate));
} }
@ -290,19 +332,8 @@ midori_extension_activate_cb (MidoriExtension* extension,
if (error->code == G_FILE_ERROR_NOENT) if (error->code == G_FILE_ERROR_NOENT)
{ {
gchar* filename = g_object_get_data (G_OBJECT (extension), "filename"); gchar* filename = g_object_get_data (G_OBJECT (extension), "filename");
gchar* folder;
if (g_str_has_prefix (filename, MIDORI_MODULE_PREFIX))
filename = &filename[strlen (MIDORI_MODULE_PREFIX)];
if (g_str_has_suffix (filename, G_MODULE_SUFFIX))
filename = g_strndup (filename,
strlen (filename) - strlen ("." G_MODULE_SUFFIX));
else
filename = g_strdup (filename);
folder = g_strconcat ("extensions/", filename, NULL);
g_free (filename);
katze_assign (config_file, katze_assign (config_file,
sokoke_find_config_filename (folder, "config")); midori_paths_get_extension_preset_filename (filename, "config"));
g_free (folder);
g_key_file_load_from_file (extension->priv->key_file, config_file, g_key_file_load_from_file (extension->priv->key_file, config_file,
G_KEY_FILE_KEEP_COMMENTS, NULL); G_KEY_FILE_KEEP_COMMENTS, NULL);
} }
@ -321,29 +352,32 @@ midori_extension_activate_cb (MidoriExtension* extension,
if (setting->type == G_TYPE_BOOLEAN) if (setting->type == G_TYPE_BOOLEAN)
{ {
MESettingBoolean* setting_ = (MESettingBoolean*)setting; MESettingBoolean* setting_ = (MESettingBoolean*)setting;
if (extension->priv->key_file) if (extension->priv->key_file
setting_->value = sokoke_key_file_get_boolean_default ( && g_key_file_has_key (extension->priv->key_file, "settings", setting_->name, NULL))
extension->priv->key_file, setting_->value = g_key_file_get_boolean (extension->priv->key_file,
"settings", setting->name, setting_->default_value, NULL); "settings", setting->name, NULL);
else else
setting_->value = setting_->default_value; setting_->value = setting_->default_value;
} }
else if (setting->type == G_TYPE_INT) else if (setting->type == G_TYPE_INT)
{ {
MESettingInteger* setting_ = (MESettingInteger*)setting; MESettingInteger* setting_ = (MESettingInteger*)setting;
if (extension->priv->key_file) if (extension->priv->key_file
setting_->value = sokoke_key_file_get_integer_default ( && g_key_file_has_key (extension->priv->key_file, "settings", setting_->name, NULL))
extension->priv->key_file, setting_->value = g_key_file_get_integer (extension->priv->key_file,
"settings", setting->name, setting_->default_value, NULL); "settings", setting_->name, NULL);
else else
setting_->value = setting_->default_value; setting_->value = setting_->default_value;
} }
else if (setting->type == G_TYPE_STRING) else if (setting->type == G_TYPE_STRING)
{ {
if (extension->priv->key_file) if (extension->priv->key_file)
setting->value = sokoke_key_file_get_string_default ( {
extension->priv->key_file, setting->value = g_key_file_get_string (
"settings", setting->name, setting->default_value, NULL); extension->priv->key_file, "settings", setting->name, NULL);
if (setting->value == NULL)
setting->value = setting->default_value;
}
else else
setting->value = g_strdup (setting->default_value); setting->value = g_strdup (setting->default_value);
} }
@ -352,10 +386,13 @@ midori_extension_activate_cb (MidoriExtension* extension,
MESettingStringList* setting_ = (MESettingStringList*)setting; MESettingStringList* setting_ = (MESettingStringList*)setting;
if (extension->priv->key_file) if (extension->priv->key_file)
{ {
setting_->value = sokoke_key_file_get_string_list_default ( setting_->value = g_key_file_get_string_list (extension->priv->key_file,
extension->priv->key_file, "settings", setting->name, &setting_->length, NULL);
"settings", setting->name, &setting_->length, if (setting_->value == NULL)
setting_->default_value, &setting_->default_length, NULL); {
setting_->value = g_strdupv (setting_->default_value);
setting_->length = setting_->default_length;
}
} }
else else
setting_->value = g_strdupv (setting_->default_value); setting_->value = g_strdupv (setting_->default_value);
@ -395,11 +432,13 @@ midori_extension_finalize (GObject* object)
MidoriExtension* extension = MIDORI_EXTENSION (object); MidoriExtension* extension = MIDORI_EXTENSION (object);
katze_object_assign (extension->priv->app, NULL); katze_object_assign (extension->priv->app, NULL);
katze_assign (extension->priv->stock_id, NULL);
katze_assign (extension->priv->name, NULL); katze_assign (extension->priv->name, NULL);
katze_assign (extension->priv->description, NULL); katze_assign (extension->priv->description, NULL);
katze_assign (extension->priv->version, NULL); katze_assign (extension->priv->version, NULL);
katze_assign (extension->priv->authors, NULL); katze_assign (extension->priv->authors, NULL);
katze_assign (extension->priv->website, NULL); katze_assign (extension->priv->website, NULL);
katze_assign (extension->priv->key, NULL);
katze_assign (extension->priv->config_dir, NULL); katze_assign (extension->priv->config_dir, NULL);
g_list_free (extension->priv->lsettings); g_list_free (extension->priv->lsettings);
@ -418,12 +457,18 @@ midori_extension_set_property (GObject* object,
switch (prop_id) switch (prop_id)
{ {
case PROP_STOCK_ID:
katze_assign (extension->priv->stock_id, g_value_dup_string (value));
break;
case PROP_NAME: case PROP_NAME:
katze_assign (extension->priv->name, g_value_dup_string (value)); katze_assign (extension->priv->name, g_value_dup_string (value));
break; break;
case PROP_DESCRIPTION: case PROP_DESCRIPTION:
katze_assign (extension->priv->description, g_value_dup_string (value)); katze_assign (extension->priv->description, g_value_dup_string (value));
break; break;
case PROP_USE_MARKUP:
extension->priv->use_markup = g_value_get_boolean (value);
break;
case PROP_VERSION: case PROP_VERSION:
{ {
/* Don't show version suffix if it matches the running Midori */ /* Don't show version suffix if it matches the running Midori */
@ -446,6 +491,9 @@ midori_extension_set_property (GObject* object,
case PROP_WEBSITE: case PROP_WEBSITE:
katze_assign (extension->priv->website, g_value_dup_string (value)); katze_assign (extension->priv->website, g_value_dup_string (value));
break; break;
case PROP_KEY:
katze_assign (extension->priv->key, g_value_dup_string (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -462,12 +510,18 @@ midori_extension_get_property (GObject* object,
switch (prop_id) switch (prop_id)
{ {
case PROP_STOCK_ID:
g_value_set_string (value, extension->priv->stock_id);
break;
case PROP_NAME: case PROP_NAME:
g_value_set_string (value, extension->priv->name); g_value_set_string (value, extension->priv->name);
break; break;
case PROP_DESCRIPTION: case PROP_DESCRIPTION:
g_value_set_string (value, extension->priv->description); g_value_set_string (value, extension->priv->description);
break; break;
case PROP_USE_MARKUP:
g_value_set_boolean (value, extension->priv->use_markup);
break;
case PROP_VERSION: case PROP_VERSION:
g_value_set_string (value, extension->priv->version); g_value_set_string (value, extension->priv->version);
break; break;
@ -477,12 +531,209 @@ midori_extension_get_property (GObject* object,
case PROP_WEBSITE: case PROP_WEBSITE:
g_value_set_string (value, extension->priv->website); g_value_set_string (value, extension->priv->website);
break; break;
case PROP_KEY:
g_value_set_string (value, extension->priv->key);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
void
midori_extension_load_from_folder (MidoriApp* app,
gchar** keys,
gboolean activate)
{
if (!g_module_supported ())
return;
gchar* extension_path = midori_paths_get_lib_path (PACKAGE_NAME);
if (!extension_path)
return;
if (activate)
{
gint i = 0;
const gchar* filename;
while (keys && (filename = keys[i++]))
midori_extension_activate_gracefully (app, extension_path, filename, activate);
/* FIXME need proper stock extension mechanism */
GObject* extension = midori_extension_activate_gracefully (app, extension_path, "libtransfers." G_MODULE_SUFFIX, activate);
g_assert (extension != NULL);
}
else
{
GDir* extension_dir = g_dir_open (extension_path, 0, NULL);
g_return_if_fail (extension_dir != NULL);
const gchar* filename;
while ((filename = g_dir_read_name (extension_dir)))
midori_extension_activate_gracefully (app, extension_path, filename, activate);
g_dir_close (extension_dir);
}
g_free (extension_path);
}
GObject*
midori_extension_load_from_file (const gchar* extension_path,
const gchar* filename,
gboolean activate,
gboolean test)
{
gchar* fullname;
GModule* module;
typedef GObject* (*extension_init_func)(void);
extension_init_func extension_init;
static GHashTable* modules = NULL;
GObject* extension;
g_return_val_if_fail (extension_path != NULL, NULL);
g_return_val_if_fail (filename != NULL, NULL);
if (strchr (filename, '/'))
{
gchar* clean = g_strndup (filename, strchr (filename, '/') - filename);
fullname = g_build_filename (extension_path, clean, NULL);
g_free (clean);
}
else
fullname = g_build_filename (extension_path, filename, NULL);
/* Ignore files which don't have the correct suffix */
if (!g_str_has_suffix (fullname, G_MODULE_SUFFIX))
{
g_free (fullname);
return NULL;
}
module = g_module_open (fullname, G_MODULE_BIND_LOCAL);
g_free (fullname);
/* GModule detects repeated loading but exposes no API to check it.
Skip any modules that were loaded before. */
if (modules == NULL)
modules = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
if ((extension = g_hash_table_lookup (modules, module)))
return extension;
if (module && g_module_symbol (module, "extension_init",
(gpointer) &extension_init))
{
typedef void (*extension_test_func)(void);
extension_test_func extension_test;
if ((extension = extension_init ()))
{
if (test && g_module_symbol (module, "extension_test", (gpointer) &extension_test))
extension_test ();
g_object_set_data_full (G_OBJECT (extension), "filename", g_strdup (filename), g_free);
g_hash_table_insert (modules, module, extension);
}
}
return extension;
}
GObject*
midori_extension_activate_gracefully (MidoriApp* app,
const gchar* extension_path,
const gchar* filename,
gboolean activate)
{
GObject* extension = midori_extension_load_from_file (extension_path, filename, activate, FALSE);
midori_extension_activate (extension, filename, activate, app);
if (!extension && g_module_error () != NULL)
{
KatzeArray* extensions = katze_object_get_object (app, "extensions");
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", filename,
"description", g_module_error (),
NULL);
g_warning ("%s", g_module_error ());
katze_array_add_item (extensions, extension);
g_object_unref (extensions);
g_object_unref (extension);
return NULL;
}
else
return extension;
}
static void
midori_extension_add_to_list (MidoriApp* app,
MidoriExtension* extension,
const gchar* filename)
{
g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (filename != NULL);
KatzeArray* extensions = katze_object_get_object (app, "extensions");
g_return_if_fail (KATZE_IS_ARRAY (extensions));
if (katze_array_get_item_index (extensions, extension) >= 0)
return;
/* FIXME need proper stock extension mechanism */
if (!strcmp (filename, "libtransfers." G_MODULE_SUFFIX))
return;
katze_array_add_item (extensions, extension);
g_object_unref (extensions);
if (midori_paths_is_readonly ())
return;
/* Signal that we want the extension to load and save */
if (midori_extension_is_prepared (extension))
{
g_warn_if_fail (extension->priv->config_dir == NULL);
extension->priv->config_dir = midori_paths_get_extension_config_dir (filename);
}
}
void
midori_extension_activate (GObject* extension,
const gchar* filename,
gboolean activate,
MidoriApp* app)
{
if (MIDORI_IS_EXTENSION (extension))
{
if (filename != NULL)
midori_extension_add_to_list (app, MIDORI_EXTENSION (extension), filename);
if (activate && !midori_extension_is_active (MIDORI_EXTENSION (extension)))
g_signal_emit_by_name (extension, "activate", app);
}
else if (KATZE_IS_ARRAY (extension))
{
gboolean success = FALSE;
MidoriExtension* extension_item;
KATZE_ARRAY_FOREACH_ITEM (extension_item, KATZE_ARRAY (extension))
if (MIDORI_IS_EXTENSION (extension_item))
{
gchar* key = extension_item->priv->key;
g_return_if_fail (key != NULL);
if (filename != NULL && strchr (filename, '/'))
{
gchar* clean = g_strndup (filename, strchr (filename, '/') - filename);
g_object_set_data_full (G_OBJECT (extension_item), "filename", clean, g_free);
midori_extension_add_to_list (app, extension_item, clean);
}
else if (filename != NULL)
{
midori_extension_add_to_list (app, extension_item, filename);
g_object_set_data_full (G_OBJECT (extension_item), "filename", g_strdup (filename), g_free);
}
if (activate && !midori_extension_is_active (MIDORI_EXTENSION (extension_item))
&& filename && strstr (filename, key))
{
g_signal_emit_by_name (extension_item, "activate", app);
success = TRUE;
}
}
/* Passed a multi extension w/o key or non-existing key */
g_warn_if_fail (!activate || success);
}
}
/** /**
* midori_extension_is_prepared: * midori_extension_is_prepared:
* @extension: a #MidoriExtension * @extension: a #MidoriExtension
@ -539,29 +790,6 @@ midori_extension_is_active (MidoriExtension* extension)
return extension->priv->active > 0; return extension->priv->active > 0;
} }
/**
* midori_extension_is_deactivating:
* @extension: a #MidoriExtension
*
* Determines if @extension is currently in the process of
* being deactivated.
*
* Extensions remain fully functional even while being
* deactivated, so you can for instance still save settings
* but you may need to cleanup during deactivation.
*
* Return value: %TRUE if @extension is deactivating
*
* Since: 0.1.7
**/
gboolean
midori_extension_is_deactivating (MidoriExtension* extension)
{
g_return_val_if_fail (MIDORI_IS_EXTENSION (extension), FALSE);
return extension->priv->active == 2;
}
/** /**
* midori_extension_deactivate: * midori_extension_deactivate:
* @extension: a #MidoriExtension * @extension: a #MidoriExtension
@ -573,7 +801,6 @@ midori_extension_deactivate (MidoriExtension* extension)
{ {
g_return_if_fail (midori_extension_is_active (extension)); g_return_if_fail (midori_extension_is_active (extension));
extension->priv->active = 2;
g_signal_emit (extension, signals[DEACTIVATE], 0); g_signal_emit (extension, signals[DEACTIVATE], 0);
extension->priv->active = 0; extension->priv->active = 0;
katze_object_assign (extension->priv->app, NULL); katze_object_assign (extension->priv->app, NULL);
@ -616,15 +843,6 @@ midori_extension_get_config_dir (MidoriExtension* extension)
g_return_val_if_fail (midori_extension_is_prepared (extension), NULL); g_return_val_if_fail (midori_extension_is_prepared (extension), NULL);
if (!extension->priv->config_dir)
{
gchar* filename = g_object_get_data (G_OBJECT (extension), "filename");
if (!filename)
return "/";
extension->priv->config_dir = g_build_filename (
sokoke_set_config_dir (NULL), "extensions", filename, NULL);
}
return extension->priv->config_dir; return extension->priv->config_dir;
} }
@ -656,6 +874,22 @@ midori_extension_install_boolean (MidoriExtension* extension,
default_value, FALSE); default_value, FALSE);
} }
static void
midori_extension_save_settings (MidoriExtension *extension)
{
GError* error = NULL;
gchar* config_file = g_build_filename (extension->priv->config_dir, "config", NULL);
katze_mkdir_with_parents (extension->priv->config_dir, 0700);
sokoke_key_file_save_to_file (extension->priv->key_file, config_file, &error);
if (error)
{
printf (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
extension->priv->name, error->message);
g_error_free (error);
}
g_free (config_file);
}
/** /**
* midori_extension_get_boolean: * midori_extension_get_boolean:
* @extension: a #MidoriExtension * @extension: a #MidoriExtension
@ -708,23 +942,14 @@ midori_extension_set_boolean (MidoriExtension* extension,
setting->value = value; setting->value = value;
if (extension->priv->key_file) if (extension->priv->key_file)
{ {
GError* error = NULL;
/* FIXME: Handle readonly folder/ file */
gchar* config_file = g_build_filename (extension->priv->config_dir,
"config", NULL);
katze_mkdir_with_parents (extension->priv->config_dir, 0700);
g_key_file_set_boolean (extension->priv->key_file, g_key_file_set_boolean (extension->priv->key_file,
"settings", name, value); "settings", name, value);
sokoke_key_file_save_to_file (extension->priv->key_file, config_file, &error); midori_extension_save_settings (extension);
if (error)
{
printf (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
extension->priv->name, error->message);
g_error_free (error);
}
} }
} }
/** /**
* midori_extension_install_integer: * midori_extension_install_integer:
* @extension: a #MidoriExtension * @extension: a #MidoriExtension
@ -805,20 +1030,9 @@ midori_extension_set_integer (MidoriExtension* extension,
setting->value = value; setting->value = value;
if (extension->priv->key_file) if (extension->priv->key_file)
{ {
GError* error = NULL;
/* FIXME: Handle readonly folder/ file */
gchar* config_file = g_build_filename (extension->priv->config_dir,
"config", NULL);
katze_mkdir_with_parents (extension->priv->config_dir, 0700);
g_key_file_set_integer (extension->priv->key_file, g_key_file_set_integer (extension->priv->key_file,
"settings", name, value); "settings", name, value);
sokoke_key_file_save_to_file (extension->priv->key_file, config_file, &error); midori_extension_save_settings (extension);
if (error)
{
printf (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
extension->priv->name, error->message);
g_error_free (error);
}
} }
} }
@ -902,20 +1116,9 @@ midori_extension_set_string (MidoriExtension* extension,
katze_assign (setting->value, g_strdup (value)); katze_assign (setting->value, g_strdup (value));
if (extension->priv->key_file) if (extension->priv->key_file)
{ {
GError* error = NULL;
/* FIXME: Handle readonly folder/ file */
gchar* config_file = g_build_filename (extension->priv->config_dir,
"config", NULL);
katze_mkdir_with_parents (extension->priv->config_dir, 0700);
g_key_file_set_string (extension->priv->key_file, g_key_file_set_string (extension->priv->key_file,
"settings", name, value); "settings", name, value);
sokoke_key_file_save_to_file (extension->priv->key_file, config_file, &error); midori_extension_save_settings (extension);
if (error)
{
printf (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
extension->priv->name, error->message);
g_error_free (error);
}
} }
} }
@ -1014,19 +1217,8 @@ midori_extension_set_string_list (MidoriExtension* extension,
if (extension->priv->key_file) if (extension->priv->key_file)
{ {
GError* error = NULL;
/* FIXME: Handle readonly folder/ file */
gchar* config_file = g_build_filename (extension->priv->config_dir,
"config", NULL);
katze_mkdir_with_parents (extension->priv->config_dir, 0700);
g_key_file_set_string_list (extension->priv->key_file, g_key_file_set_string_list (extension->priv->key_file,
"settings", name, (const gchar**)value, length); "settings", name, (const gchar**)value, length);
sokoke_key_file_save_to_file (extension->priv->key_file, config_file, &error); midori_extension_save_settings (extension);
if (error)
{
printf (_("The configuration of the extension '%s' couldn't be saved: %s\n"),
extension->priv->name, error->message);
g_error_free (error);
}
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2008 Christian Dywan <christian@twotoasts.de> Copyright (C) 2008-2012 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -48,6 +48,24 @@ struct _MidoriExtensionClass
GType GType
midori_extension_get_type (void) G_GNUC_CONST; midori_extension_get_type (void) G_GNUC_CONST;
GObject*
midori_extension_load_from_file (const gchar* extension_path,
const gchar* filename,
gboolean activate,
gboolean test);
void
midori_extension_activate (GObject* extension,
const gchar* filename,
gboolean activate,
MidoriApp* app);
GObject*
midori_extension_activate_gracefully (MidoriApp* app,
const gchar* extension_path,
const gchar* filename,
gboolean activate);
gboolean gboolean
midori_extension_is_prepared (MidoriExtension* extension); midori_extension_is_prepared (MidoriExtension* extension);
@ -57,9 +75,6 @@ midori_extension_has_preferences (MidoriExtension* extension);
gboolean gboolean
midori_extension_is_active (MidoriExtension* extension); midori_extension_is_active (MidoriExtension* extension);
gboolean
midori_extension_is_deactivating (MidoriExtension* extension);
void void
midori_extension_deactivate (MidoriExtension* extension); midori_extension_deactivate (MidoriExtension* extension);
@ -128,6 +143,11 @@ midori_extension_set_string_list (MidoriExtension* extension,
gchar** value, gchar** value,
gsize length); gsize length);
void
midori_extension_load_from_folder (MidoriApp* app,
gchar** keys,
gboolean activate);
G_END_DECLS G_END_DECLS
#endif /* __MIDORI_EXTENSION_H__ */ #endif /* __MIDORI_EXTENSION_H__ */

View file

@ -1,3 +1,13 @@
/*
Copyright (C) 2012 André Stösel <andre@stoesel.de>
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.
*/
public class Midori.ExtensionsColumn : Gtk.TreeViewColumn { public class Midori.ExtensionsColumn : Gtk.TreeViewColumn {
public signal void row_clicked (Gtk.TreeView view, Gtk.TreePath path); public signal void row_clicked (Gtk.TreeView view, Gtk.TreePath path);

616
midori/midori-frontend.c Normal file
View file

@ -0,0 +1,616 @@
/*
Copyright (C) 2008-2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#include "midori-array.h"
#include "midori-bookmarks.h"
#include "midori-history.h"
#include "midori-preferences.h"
#include "midori-privatedata.h"
#include "midori-session.h"
#include "midori-searchaction.h"
#include "midori-panel.h"
#include "panels/midori-bookmarks.h"
#include "panels/midori-history.h"
#include "sokoke.h"
#include <glib/gi18n-lib.h>
static MidoriBrowser*
midori_frontend_browser_new_window_cb (MidoriBrowser* browser,
MidoriBrowser* new_browser,
gpointer user_data)
{
if (new_browser == NULL)
new_browser = midori_browser_new ();
g_object_set (new_browser,
"settings", midori_browser_get_settings (browser),
NULL);
gtk_widget_show (GTK_WIDGET (new_browser));
return new_browser;
}
MidoriBrowser*
midori_web_app_new (const gchar* config,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris)
{
guint i;
midori_paths_init (MIDORI_RUNTIME_MODE_APP, config);
#ifndef HAVE_WEBKIT2
g_object_set_data (G_OBJECT (webkit_get_default_session ()), "pass-through-console", (void*)1);
#endif
MidoriBrowser* browser = midori_browser_new ();
g_signal_connect (browser, "new-window",
G_CALLBACK (midori_frontend_browser_new_window_cb), NULL);
midori_browser_set_action_visible (browser, "Menubar", FALSE);
midori_browser_set_action_visible (browser, "CompactMenu", FALSE);
MidoriWebSettings* settings = midori_browser_get_settings (browser);
g_object_set (settings,
"show-menubar", FALSE,
"show-navigationbar", FALSE,
"toolbar-items", "Back,Forward,ReloadStop,Location,Homepage",
"show-statusbar", FALSE,
"show-panel", FALSE,
"last-window-state", MIDORI_WINDOW_NORMAL,
"inactivity-reset", inactivity_reset,
"block-uris", block_uris,
NULL);
midori_load_soup_session (settings);
KatzeArray* search_engines = midori_search_engines_new_from_folder (NULL);
g_object_set (browser,
"show-tabs", open_uris != NULL,
"settings", settings,
NULL);
midori_browser_set_action_visible (browser, "Panel", FALSE);
g_object_unref (search_engines);
if (webapp != NULL)
{
gchar* tmp_uri = sokoke_magic_uri (webapp, FALSE, TRUE);
g_object_set (settings, "homepage", tmp_uri, NULL);
midori_browser_add_uri (browser, tmp_uri);
g_free (tmp_uri);
}
for (i = 0; open_uris && open_uris[i]; i++)
{
gchar* new_uri = sokoke_magic_uri (open_uris[i], FALSE, TRUE);
midori_browser_add_uri (browser, new_uri);
g_free (new_uri);
}
if (midori_browser_get_n_pages (browser) == 0)
midori_browser_add_uri (browser, "about:blank");
gtk_widget_show (GTK_WIDGET (browser));
for (i = 0; execute_commands && execute_commands[i]; i++)
{
midori_browser_assert_action (browser, execute_commands[i]);
midori_browser_activate_action (browser, execute_commands[i]);
}
return browser;
}
static void
midori_trash_add_item_no_save_cb (KatzeArray* trash,
GObject* item)
{
if (katze_array_get_nth_item (trash, 10))
{
KatzeItem* obsolete_item = katze_array_get_nth_item (trash, 0);
katze_array_remove_item (trash, obsolete_item);
}
}
static void
midori_trash_remove_item_cb (KatzeArray* trash,
GObject* item)
{
gchar* config_file = midori_paths_get_config_filename_for_writing ("tabtrash.xbel");
GError* error = NULL;
midori_trash_add_item_no_save_cb (trash, item);
if (!midori_array_to_file (trash, config_file, "xbel-tiny", &error))
{
/* i18n: Trash, or wastebin, containing closed tabs */
g_warning (_("The trash couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_trash_add_item_cb (KatzeArray* trash,
GObject* item)
{
midori_trash_remove_item_cb (trash, item);
}
MidoriBrowser*
midori_private_app_new (const gchar* config,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris)
{
guint i;
midori_paths_init (MIDORI_RUNTIME_MODE_PRIVATE, config);
#ifndef HAVE_WEBKIT2
g_object_set_data (G_OBJECT (webkit_get_default_session ()), "pass-through-console", (void*)1);
#endif
/* Mask the timezone, which can be read by Javascript */
g_setenv ("TZ", "UTC", TRUE);
MidoriBrowser* browser = midori_browser_new ();
g_signal_connect (browser, "new-window",
G_CALLBACK (midori_frontend_browser_new_window_cb), NULL);
MidoriWebSettings* settings = midori_settings_new_full (NULL);
g_object_set (settings,
"preferred-languages", "en",
"enable-private-browsing", TRUE,
#ifdef HAVE_LIBSOUP_2_29_91
"first-party-cookies-only", TRUE,
#endif
"enable-html5-database", FALSE,
"enable-html5-local-storage", FALSE,
"enable-offline-web-application-cache", FALSE,
/* Arguably DNS prefetching is or isn't a privacy concern. For the
* lack of more fine-grained control we'll go the safe route. */
#if WEBKIT_CHECK_VERSION (1, 3, 11)
"enable-dns-prefetching", FALSE,
#endif
"strip-referer", TRUE,
"show-panel", FALSE,
"last-window-state", MIDORI_WINDOW_NORMAL,
"inactivity-reset", inactivity_reset,
"block-uris", block_uris,
NULL);
midori_load_soup_session (settings);
/* In-memory trash for re-opening closed tabs */
KatzeArray* trash = katze_array_new (KATZE_TYPE_ITEM);
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_trash_add_item_no_save_cb), NULL);
KatzeArray* search_engines = midori_search_engines_new_from_folder (NULL);
g_object_set (browser,
"settings", settings,
"trash", trash,
"search-engines", search_engines,
NULL);
g_object_unref (settings);
g_object_unref (trash);
g_object_unref (search_engines);
midori_browser_set_action_visible (browser, "Tools", FALSE);
midori_browser_set_action_visible (browser, "ClearPrivateData", FALSE);
midori_browser_set_action_visible (browser, "Panel", FALSE);
#if GTK_CHECK_VERSION (3, 0, 0)
g_object_set (gtk_widget_get_settings (GTK_WIDGET (browser)),
"gtk-application-prefer-dark-theme", TRUE,
NULL);
#endif
if (webapp != NULL)
{
gchar* tmp_uri = sokoke_magic_uri (webapp, FALSE, TRUE);
g_object_set (settings, "homepage", tmp_uri, NULL);
midori_browser_add_uri (browser, tmp_uri);
g_free (tmp_uri);
}
for (i = 0; open_uris && open_uris[i]; i++)
{
gchar* new_uri = sokoke_magic_uri (open_uris[i], FALSE, TRUE);
midori_browser_add_uri (browser, new_uri);
g_free (new_uri);
}
if (midori_browser_get_n_pages (browser) == 0)
midori_browser_add_uri (browser, "about:private");
gtk_widget_show (GTK_WIDGET (browser));
for (i = 0; execute_commands && execute_commands[i]; i++)
{
midori_browser_assert_action (browser, execute_commands[i]);
midori_browser_activate_action (browser, execute_commands[i]);
}
/* FIXME need proper stock extension mechanism */
midori_browser_activate_action (browser, "libtransfers." G_MODULE_SUFFIX "=true");
g_assert (g_module_error () == NULL);
return browser;
}
static void
midori_browser_show_preferences_cb (MidoriBrowser* browser,
KatzePreferences* preferences,
MidoriApp* app)
{
midori_preferences_add_extension_category (preferences, app);
}
static void
midori_browser_privacy_preferences_cb (MidoriBrowser* browser,
KatzePreferences* preferences,
MidoriApp* app)
{
MidoriWebSettings* settings = midori_browser_get_settings (browser);
midori_preferences_add_privacy_category (preferences, settings);
}
static void
midori_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
gpointer user_data)
{
GtkWidget* panel;
GtkWidget* addon;
panel = katze_object_get_object (browser, "panel");
addon = g_object_new (MIDORI_TYPE_BOOKMARKS, "app", app, "visible", TRUE, NULL);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
addon = g_object_new (MIDORI_TYPE_HISTORY, "app", app, "visible", TRUE, NULL);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Extensions */
g_signal_connect (browser, "show-preferences",
G_CALLBACK (midori_browser_privacy_preferences_cb), app);
g_signal_connect (browser, "show-preferences",
G_CALLBACK (midori_browser_show_preferences_cb), app);
g_object_unref (panel);
}
static void
button_disable_extensions_clicked_cb (GtkWidget* button,
MidoriApp* app)
{
/* Reset frozen list of active extensions */
g_object_set_data (G_OBJECT (app), "extensions", NULL);
gtk_widget_set_sensitive (button, FALSE);
}
static void
button_modify_preferences_clicked_cb (GtkWidget* button,
MidoriWebSettings* settings)
{
GtkWidget* dialog = midori_preferences_new (
GTK_WINDOW (gtk_widget_get_toplevel (button)), settings);
if (midori_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
}
static void
midori_frontend_crash_log_cb (GtkWidget* button,
gchar* crash_log)
{
GError* error = NULL;
if (!sokoke_show_uri (gtk_widget_get_screen (button), crash_log, 0, &error))
{
sokoke_message_dialog (GTK_MESSAGE_ERROR,
_("Could not run external program."),
error->message, FALSE);
g_error_free (error);
}
}
static void
midori_frontend_debugger_cb (GtkWidget* button,
GtkDialog* dialog)
{
gtk_dialog_response (dialog, GTK_RESPONSE_HELP);
}
static MidoriStartup
midori_frontend_diagnostic_dialog (MidoriApp* app,
MidoriWebSettings* settings,
KatzeArray* session)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* align;
GtkWidget* box;
GtkWidget* button;
MidoriStartup load_on_startup = katze_object_get_enum (settings, "load-on-startup");
gint response;
dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
_("Midori crashed the last time it was opened. You can report the problem at %s."),
PACKAGE_BUGREPORT);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
gtk_window_set_title (GTK_WINDOW (dialog), g_get_application_name ());
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
align = gtk_alignment_new (0.5, 0.5, 0.5, 0.5);
gtk_container_add (GTK_CONTAINER (content_area), align);
box = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (align), box);
button = gtk_button_new_with_mnemonic (_("Modify _preferences"));
g_signal_connect (button, "clicked",
G_CALLBACK (button_modify_preferences_clicked_cb), settings);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
button = gtk_button_new_with_mnemonic (_("Disable all _extensions"));
if (g_object_get_data (G_OBJECT (app), "extensions"))
g_signal_connect (button, "clicked",
G_CALLBACK (button_disable_extensions_clicked_cb), app);
else
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
gtk_widget_show_all (align);
button = katze_property_proxy (settings, "show-crash-dialog", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Show a dialog after Midori crashed"));
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (content_area), button);
gtk_container_set_focus_child (GTK_CONTAINER (dialog), gtk_dialog_get_action_area (GTK_DIALOG (dialog)));
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("Discard old tabs"), MIDORI_STARTUP_BLANK_PAGE,
_("Show last tabs without loading"), MIDORI_STARTUP_DELAYED_PAGES,
_("Show last open tabs"), MIDORI_STARTUP_LAST_OPEN_PAGES,
NULL);
gchar* crash_log = g_build_filename (midori_paths_get_runtime_dir (), "gdb.bt", NULL);
if (g_access (crash_log, F_OK) == 0)
{
GtkWidget* button = gtk_button_new_with_mnemonic (_("Show last crash _log"));
g_signal_connect_data (button, "clicked",
G_CALLBACK (midori_frontend_crash_log_cb), crash_log,
(GClosureNotify)g_free, 0);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
}
else
g_free (crash_log);
gchar* gdb = g_find_program_in_path ("gdb");
if (gdb != NULL)
{
GtkWidget* button = gtk_button_new_with_mnemonic (_("Run in _debugger"));
g_signal_connect (button, "clicked",
G_CALLBACK (midori_frontend_debugger_cb), dialog);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
}
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
load_on_startup == MIDORI_STARTUP_HOMEPAGE
? MIDORI_STARTUP_BLANK_PAGE : load_on_startup);
/* GtkLabel can't wrap the text properly. Until some day
this works, we implement this hack to do it ourselves. */
GList* ch = gtk_container_get_children (GTK_CONTAINER (content_area));
GtkWidget* hbox = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (hbox));
GtkWidget* vbox = (GtkWidget*)g_list_nth_data (ch, 1);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (vbox));
GtkWidget* label = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
GtkRequisition req;
gtk_widget_size_request (content_area, &req);
gtk_widget_set_size_request (label, req.width * 0.9, -1);
response = midori_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_DELETE_EVENT)
response = G_MAXINT;
else if (response == GTK_RESPONSE_HELP)
{
sokoke_spawn_gdb (gdb, FALSE);
response = G_MAXINT;
}
else if (response == MIDORI_STARTUP_BLANK_PAGE)
katze_array_clear (session);
return response;
}
MidoriApp*
midori_normal_app_new (const gchar* config,
gchar* nickname,
gboolean diagnostic_dialog,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris)
{
if (g_str_has_suffix (nickname, "portable"))
midori_paths_init (MIDORI_RUNTIME_MODE_PORTABLE, config);
else if (g_str_has_suffix (nickname, "normal"))
midori_paths_init (MIDORI_RUNTIME_MODE_NORMAL, config);
else
g_assert_not_reached ();
MidoriApp* app = midori_app_new (nickname);
if (midori_app_instance_is_running (app))
{
/* It makes no sense to show a crash dialog while running */
if (!diagnostic_dialog)
{
gboolean success = FALSE;
if (execute_commands != NULL && midori_app_send_command (app, execute_commands))
success = TRUE;
if (open_uris != NULL && midori_app_instance_send_uris (app, open_uris))
success = TRUE;
if (!execute_commands && !open_uris && midori_app_instance_send_new_browser (app))
success = TRUE;
if (success)
return NULL;
}
/* FIXME: We mustn't lose the URL here; either instance is freezing or inside a crash dialog */
sokoke_message_dialog (GTK_MESSAGE_INFO,
_("An instance of Midori is already running but not responding.\n"),
open_uris ? *open_uris : "", TRUE);
return (void*)0xdeadbeef;
}
GString* error_messages = g_string_new (NULL);
GError* error = NULL;
gchar** extensions;
MidoriWebSettings* settings = midori_settings_new_full (&extensions);
g_object_set (settings,
"enable-developer-extras", TRUE,
"enable-html5-database", TRUE,
"block-uris", block_uris,
NULL);
if (inactivity_reset > 0)
g_object_set (settings, "inactivity-reset", inactivity_reset, NULL);
KatzeArray* search_engines = midori_search_engines_new_from_folder (error_messages);
/* Pick first search engine as default if not set */
gchar* uri = katze_object_get_string (settings, "location-entry-search");
if (!(uri && *uri) && !katze_array_is_empty (search_engines))
{
KatzeItem* item = katze_array_get_nth_item (search_engines, 0);
g_object_set (settings, "location-entry-search",
katze_item_get_uri (item), NULL);
}
g_free (uri);
KatzeArray* bookmarks;
gchar* errmsg = NULL;
if (!(bookmarks = midori_bookmarks_new (&errmsg)))
{
g_string_append_printf (error_messages,
_("Bookmarks couldn't be loaded: %s\n"), errmsg);
katze_assign (errmsg, NULL);
}
gchar* config_file = NULL;
KatzeArray* session = katze_array_new (KATZE_TYPE_ITEM);
MidoriStartup load_on_startup = katze_object_get_enum (settings, "load-on-startup");
if (load_on_startup >= MIDORI_STARTUP_LAST_OPEN_PAGES)
{
katze_assign (config_file, midori_paths_get_config_filename_for_reading ("session.xbel"));
error = NULL;
if (!midori_array_from_file (session, config_file, "xbel-tiny", &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The session couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
}
KatzeArray* trash = katze_array_new (KATZE_TYPE_ITEM);
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_trash_add_item_cb), NULL);
g_signal_connect_after (trash, "remove-item",
G_CALLBACK (midori_trash_remove_item_cb), NULL);
katze_assign (config_file, g_build_filename (config, "tabtrash.xbel", NULL));
error = NULL;
if (!midori_array_from_file (trash, config_file, "xbel-tiny", &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The trash couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
KatzeArray* history;
if (!(history = midori_history_new (&errmsg)))
{
g_string_append_printf (error_messages,
_("The history couldn't be loaded: %s\n"), errmsg);
katze_assign (errmsg, NULL);
}
katze_assign (config_file, midori_paths_get_config_filename_for_reading ("speeddial"));
MidoriSpeedDial* dial = midori_speed_dial_new (config_file, NULL);
if (error_messages->len)
{
GtkWidget* dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
_("The following errors occured:"));
gtk_message_dialog_format_secondary_text (
GTK_MESSAGE_DIALOG (dialog), "%s", error_messages->str);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
_("_Ignore"), GTK_RESPONSE_ACCEPT,
NULL);
if (midori_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
return (void*)0xdeadbeef;
gtk_widget_destroy (dialog);
}
g_string_free (error_messages, TRUE);
g_object_set_data (G_OBJECT (app), "execute-commands", execute_commands);
g_object_set_data (G_OBJECT (app), "open-uris", open_uris);
g_object_set_data_full (G_OBJECT (app), "extensions", extensions, (GDestroyNotify)g_strfreev);
katze_item_set_parent (KATZE_ITEM (session), app);
katze_assign (config_file, midori_paths_get_config_filename_for_reading ("search"));
midori_search_engines_set_filename (search_engines, config_file);
if ((midori_app_get_crashed (app)
&& katze_object_get_boolean (settings, "show-crash-dialog")
&& open_uris && !execute_commands)
|| diagnostic_dialog)
{
load_on_startup = midori_frontend_diagnostic_dialog (app, settings, session);
if (load_on_startup == G_MAXINT)
return NULL;
}
g_object_set_data (G_OBJECT (settings), "load-on-startup", GINT_TO_POINTER (load_on_startup));
g_object_set (app, "settings", settings,
"bookmarks", bookmarks,
"trash", trash,
"search-engines", search_engines,
"history", history,
"speed-dial", dial,
NULL);
g_signal_connect (app, "add-browser",
G_CALLBACK (midori_app_add_browser_cb), NULL);
g_idle_add (midori_load_soup_session_full, settings);
g_idle_add (midori_load_extensions, app);
g_idle_add (midori_load_session, session);
return app;
}
void
midori_normal_app_on_quit (MidoriApp* app)
{
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
KatzeArray* bookmarks = katze_object_get_object (app, "bookmarks");
KatzeArray* history = katze_object_get_object (app, "history");
g_object_notify (G_OBJECT (settings), "load-on-startup");
midori_bookmarks_on_quit (bookmarks);
midori_history_on_quit (history, settings);
midori_private_data_on_quit (settings);
/* Removing KatzeHttpCookies makes it save outstanding changes */
#ifndef HAVE_WEBKIT2
soup_session_remove_feature_by_type (webkit_get_default_session (),
KATZE_TYPE_HTTP_COOKIES);
#endif
MidoriStartup load_on_startup = katze_object_get_int (settings, "load-on-startup");
if (load_on_startup < MIDORI_STARTUP_LAST_OPEN_PAGES)
{
gchar* config_file = midori_paths_get_config_filename_for_writing ("session.xbel");
g_unlink (config_file);
}
}

49
midori/midori-frontend.h Normal file
View file

@ -0,0 +1,49 @@
/*
Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
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.
*/
#ifndef __MIDORI_FRONTEND_H__
#define __MIDORI_FRONTEND_H__
#include "midori/midori-app.h"
MidoriBrowser*
midori_web_app_new (const gchar* config,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris);
MidoriBrowser*
midori_private_app_new (const gchar* config,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris);
MidoriApp*
midori_normal_app_new (const gchar* config,
gchar* nickname,
gboolean diagnostic_dialog,
const gchar* webapp,
gchar** open_uris,
gchar** execute_commands,
gint inactivity_reset,
const gchar* block_uris);
void
midori_normal_app_on_quit (MidoriApp* app);
G_END_DECLS
#endif /* __MIDORI_FRONTEND_H__ */

Some files were not shown because too many files have changed in this diff Show more