Merge commit 'upstream/0.1.7'

This commit is contained in:
Ryan Niebur 2009-05-28 23:14:55 -07:00
commit 96c6e3008d
77 changed files with 23649 additions and 7918 deletions

View file

@ -15,6 +15,8 @@ Contributors:
Nick Schermer <nick@xfce.org>
Matthias Kruk <mkruk@matthiaskruk.de>
Johannes Reinhardt <jreinhardt@ist-dein-freund.de>
Jean-François Guchens <zcx000@gmail.com>
Jérôme Geulfucci <jeromeg@xfce.org>
Graphics:
extension: Nancy Runge <nancy@twotoasts.de>
@ -23,6 +25,7 @@ Graphics:
logo-shade: Nancy Runge <nancy@twotoasts.de>
Translations:
cs: David Stancl <dstancl@dstancl.cz>
da: Per Kongstad <p_kongstad@op.pl>
de: Enrico Tröger <enrico.troeger@uvena.de>
de: Christian Dywan <christian@twotoasts.de>
@ -39,6 +42,7 @@ Translations:
fr: Adrien Nader <camaradetux@gmail.com>
fr: Robert-André Mauchin <zebob.m@pengzone.org>
fr: Pascal Gervais <pggervais@yahoo.ca>
fr: Jérôme Geulfucci <jeromeg@xfce.org>
gl: Miguel Anxo Bouzada <mbouzada@gmail.com>
he: Shlomi Israel <sijproject@gmail.com>
hu: SZERVÁC Attila <sas@321.hu>
@ -49,8 +53,11 @@ Translations:
pl: Przemysław Sitek <el.pescado@gazeta.pl>
pl: Lukasz Romanowicz <romanowicz88@gmail.com>
pt_PT: Sérgio Marques <smarquespt@gmail.com>
ro: Igor Știrbu <igor.stirbu@gmail.com>
ro: Mișu Moldovan <dumol@gnome.ro>
ru: Troitskiy Nikita <niktr@mail.ru>
ru: Anton Shestakov <engored@ya.ru>
sk: Robert Hartl <hartl.robert@gmail.com>
sv: Mikael Magnusson <mikachu@comhem.se>
tr: Mirat Can Bayrak <MiratCanBayrak@gmail.com>
uk: Dmitry Nikitin <luckas_fb@mail.ru>

View file

@ -1,5 +1,23 @@
This file is licensed under the terms of the expat license, see the file EXPAT.
v0.1.7:
+ Save the activation status of extensions
+ Catch and ignore mouse buttons meant for horizontal scrolling
+ Improve panel detaching and how panels handle it
+ Add a Feed Panel extension
+ Add a Fixed-width Font Family preference
+ Support spell checking
+ Implement (optional) Speed dial feature
+ Support nicer error pages with WebKitGTK+ 1.1.6
+ Implement middle click to open menu items in tabs
+ Implement -s, --snapshot command line switch
+ Use libnotify (runtime dependency) for finished transfers
+ Add a Go button to the address entry
+ Always append tabs opened via middle/ double click on the tab bar
+ Implement Open new pages in: New window preference
+ Implement inline find with direct '.' and '/' hotkeys
+ Add basic support for @-moz-document in user styles
v0.1.6:
+ Add Delete All to transferbar
+ Show search in context menu

2
README
View file

@ -13,7 +13,7 @@ Midori is a lightweight web browser.
Requirements: GTK+ 2.10, WebkitGTK+ 1.1.1, libXML2, libsoup 2.25.2
Optional: Unique 0.9, libidn, sqlite 3.0, docutils
Optional: Unique 0.9, libidn, sqlite 3.0, docutils, libnotify
For installation instructions read INSTALL.

81
data/error.html Normal file
View file

@ -0,0 +1,81 @@
<!--
Error page template for Midori.
This file is licensed under the terms of the expat license, see the file EXPAT.
-->
<html>
<head>
<title>{title}</title>
<style type="text/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;
}
#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>
<body>
<div id="container">
<img id="logo" src="{res}/logo-shade.png" />
<img id="icon" src="{stock}/gtk-dialog-error" />
<div id="main">
<h1>{title}</h1>
<p id="message">{message}</p>
<p id="description">{description}</p>
<form onsubmit="location.reload()">
<button>
<img src="{stock}/gtk-refresh"/>
<span>{tryagain}</span>
</button>
</form>
</div>
<br style="clear: both;"/>
</div>
</body>
</html>

313
data/mootools.js Normal file
View file

@ -0,0 +1,313 @@
//MooTools, <http://mootools.net>, My Object Oriented (JavaScript) Tools. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
var MooTools={version:"1.2.2",build:"f0491d62fbb7e906789aa3733d6a67d43e5af7c9"};var Native=function(k){k=k||{};var a=k.name;var i=k.legacy;var b=k.protect;
var c=k.implement;var h=k.generics;var f=k.initialize;var g=k.afterImplement||function(){};var d=f||i;h=h!==false;d.constructor=Native;d.$family={name:"native"};
if(i&&f){d.prototype=i.prototype;}d.prototype.constructor=d;if(a){var e=a.toLowerCase();d.prototype.$family={name:e};Native.typize(d,e);}var j=function(n,l,o,m){if(!b||m||!n.prototype[l]){n.prototype[l]=o;
}if(h){Native.genericize(n,l,b);}g.call(n,l,o);return n;};d.alias=function(n,l,o){if(typeof n=="string"){if((n=this.prototype[n])){return j(this,l,n,o);
}}for(var m in n){this.alias(m,n[m],l);}return this;};d.implement=function(m,l,o){if(typeof m=="string"){return j(this,m,l,o);}for(var n in m){j(this,n,m[n],l);
}return this;};if(c){d.implement(c);}return d;};Native.genericize=function(b,c,a){if((!a||!b[c])&&typeof b.prototype[c]=="function"){b[c]=function(){var d=Array.prototype.slice.call(arguments);
return b.prototype[c].apply(d.shift(),d);};}};Native.implement=function(d,c){for(var b=0,a=d.length;b<a;b++){d[b].implement(c);}};Native.typize=function(a,b){if(!a.type){a.type=function(c){return($type(c)===b);
};}};(function(){var a={Array:Array,Date:Date,Function:Function,Number:Number,RegExp:RegExp,String:String};for(var h in a){new Native({name:h,initialize:a[h],protect:true});
}var d={"boolean":Boolean,"native":Native,object:Object};for(var c in d){Native.typize(d[c],c);}var f={Array:["concat","indexOf","join","lastIndexOf","pop","push","reverse","shift","slice","sort","splice","toString","unshift","valueOf"],String:["charAt","charCodeAt","concat","indexOf","lastIndexOf","match","replace","search","slice","split","substr","substring","toLowerCase","toUpperCase","valueOf"]};
for(var e in f){for(var b=f[e].length;b--;){Native.genericize(window[e],f[e][b],true);}}})();var Hash=new Native({name:"Hash",initialize:function(a){if($type(a)=="hash"){a=$unlink(a.getClean());
}for(var b in a){this[b]=a[b];}return this;}});Hash.implement({forEach:function(b,c){for(var a in this){if(this.hasOwnProperty(a)){b.call(c,this[a],a,this);
}}},getClean:function(){var b={};for(var a in this){if(this.hasOwnProperty(a)){b[a]=this[a];}}return b;},getLength:function(){var b=0;for(var a in this){if(this.hasOwnProperty(a)){b++;
}}return b;}});Hash.alias("forEach","each");Array.implement({forEach:function(c,d){for(var b=0,a=this.length;b<a;b++){c.call(d,this[b],b,this);}}});Array.alias("forEach","each");
function $A(b){if(b.item){var a=b.length,c=new Array(a);while(a--){c[a]=b[a];}return c;}return Array.prototype.slice.call(b);}function $arguments(a){return function(){return arguments[a];
};}function $chk(a){return !!(a||a===0);}function $clear(a){clearTimeout(a);clearInterval(a);return null;}function $defined(a){return(a!=undefined);}function $each(c,b,d){var a=$type(c);
((a=="arguments"||a=="collection"||a=="array")?Array:Hash).each(c,b,d);}function $empty(){}function $extend(c,a){for(var b in (a||{})){c[b]=a[b];}return c;
}function $H(a){return new Hash(a);}function $lambda(a){return(typeof a=="function")?a:function(){return a;};}function $merge(){var a=Array.slice(arguments);
a.unshift({});return $mixin.apply(null,a);}function $mixin(e){for(var d=1,a=arguments.length;d<a;d++){var b=arguments[d];if($type(b)!="object"){continue;
}for(var c in b){var g=b[c],f=e[c];e[c]=(f&&$type(g)=="object"&&$type(f)=="object")?$mixin(f,g):$unlink(g);}}return e;}function $pick(){for(var b=0,a=arguments.length;
b<a;b++){if(arguments[b]!=undefined){return arguments[b];}}return null;}function $random(b,a){return Math.floor(Math.random()*(a-b+1)+b);}function $splat(b){var a=$type(b);
return(a)?((a!="array"&&a!="arguments")?[b]:b):[];}var $time=Date.now||function(){return +new Date;};function $try(){for(var b=0,a=arguments.length;b<a;
b++){try{return arguments[b]();}catch(c){}}return null;}function $type(a){if(a==undefined){return false;}if(a.$family){return(a.$family.name=="number"&&!isFinite(a))?false:a.$family.name;
}if(a.nodeName){switch(a.nodeType){case 1:return"element";case 3:return(/\S/).test(a.nodeValue)?"textnode":"whitespace";}}else{if(typeof a.length=="number"){if(a.callee){return"arguments";
}else{if(a.item){return"collection";}}}}return typeof a;}function $unlink(c){var b;switch($type(c)){case"object":b={};for(var e in c){b[e]=$unlink(c[e]);
}break;case"hash":b=new Hash(c);break;case"array":b=[];for(var d=0,a=c.length;d<a;d++){b[d]=$unlink(c[d]);}break;default:return c;}return b;}var Browser=$merge({Engine:{name:"unknown",version:0},Platform:{name:(window.orientation!=undefined)?"ipod":(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase()},Features:{xpath:!!(document.evaluate),air:!!(window.runtime),query:!!(document.querySelector)},Plugins:{},Engines:{presto:function(){return(!window.opera)?false:((arguments.callee.caller)?960:((document.getElementsByClassName)?950:925));
},trident:function(){return(!window.ActiveXObject)?false:((window.XMLHttpRequest)?5:4);},webkit:function(){return(navigator.taintEnabled)?false:((Browser.Features.xpath)?((Browser.Features.query)?525:420):419);
},gecko:function(){return(document.getBoxObjectFor==undefined)?false:((document.getElementsByClassName)?19:18);}}},Browser||{});Browser.Platform[Browser.Platform.name]=true;
Browser.detect=function(){for(var b in this.Engines){var a=this.Engines[b]();if(a){this.Engine={name:b,version:a};this.Engine[b]=this.Engine[b+a]=true;
break;}}return{name:b,version:a};};Browser.detect();Browser.Request=function(){return $try(function(){return new XMLHttpRequest();},function(){return new ActiveXObject("MSXML2.XMLHTTP");
});};Browser.Features.xhr=!!(Browser.Request());Browser.Plugins.Flash=(function(){var a=($try(function(){return navigator.plugins["Shockwave Flash"].description;
},function(){return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");})||"0 r0").match(/\d+/g);return{version:parseInt(a[0]||0+"."+a[1],10)||0,build:parseInt(a[2],10)||0};
})();function $exec(b){if(!b){return b;}if(window.execScript){window.execScript(b);}else{var a=document.createElement("script");a.setAttribute("type","text/javascript");
a[(Browser.Engine.webkit&&Browser.Engine.version<420)?"innerText":"text"]=b;document.head.appendChild(a);document.head.removeChild(a);}return b;}Native.UID=1;
var $uid=(Browser.Engine.trident)?function(a){return(a.uid||(a.uid=[Native.UID++]))[0];}:function(a){return a.uid||(a.uid=Native.UID++);};var Window=new Native({name:"Window",legacy:(Browser.Engine.trident)?null:window.Window,initialize:function(a){$uid(a);
if(!a.Element){a.Element=$empty;if(Browser.Engine.webkit){a.document.createElement("iframe");}a.Element.prototype=(Browser.Engine.webkit)?window["[[DOMElement.prototype]]"]:{};
}a.document.window=a;return $extend(a,Window.Prototype);},afterImplement:function(b,a){window[b]=Window.Prototype[b]=a;}});Window.Prototype={$family:{name:"window"}};
new Window(window);var Document=new Native({name:"Document",legacy:(Browser.Engine.trident)?null:window.Document,initialize:function(a){$uid(a);a.head=a.getElementsByTagName("head")[0];
a.html=a.getElementsByTagName("html")[0];if(Browser.Engine.trident&&Browser.Engine.version<=4){$try(function(){a.execCommand("BackgroundImageCache",false,true);
});}if(Browser.Engine.trident){a.window.attachEvent("onunload",function(){a.window.detachEvent("onunload",arguments.callee);a.head=a.html=a.window=null;
});}return $extend(a,Document.Prototype);},afterImplement:function(b,a){document[b]=Document.Prototype[b]=a;}});Document.Prototype={$family:{name:"document"}};
new Document(document);Array.implement({every:function(c,d){for(var b=0,a=this.length;b<a;b++){if(!c.call(d,this[b],b,this)){return false;}}return true;
},filter:function(d,e){var c=[];for(var b=0,a=this.length;b<a;b++){if(d.call(e,this[b],b,this)){c.push(this[b]);}}return c;},clean:function(){return this.filter($defined);
},indexOf:function(c,d){var a=this.length;for(var b=(d<0)?Math.max(0,a+d):d||0;b<a;b++){if(this[b]===c){return b;}}return -1;},map:function(d,e){var c=[];
for(var b=0,a=this.length;b<a;b++){c[b]=d.call(e,this[b],b,this);}return c;},some:function(c,d){for(var b=0,a=this.length;b<a;b++){if(c.call(d,this[b],b,this)){return true;
}}return false;},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];}return d;},link:function(c){var a={};
for(var e=0,b=this.length;e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;
},extend:function(c){for(var b=0,a=c.length;b<a;b++){this.push(c[b]);}return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[$random(0,this.length-1)]:null;
},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
},erase:function(b){for(var a=this.length;a--;a){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
for(var b=0,a=this.length;b<a;b++){var c=$type(this[b]);if(!c){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments")?Array.flatten(this[b]):this[b]);
}return d;},hexToRgb:function(b){if(this.length!=3){return null;}var a=this.map(function(c){if(c.length==1){c+=c;}return c.toInt(16);});return(b)?a:"rgb("+a+")";
},rgbToHex:function(d){if(this.length<3){return null;}if(this.length==4&&this[3]==0&&!d){return"transparent";}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);
b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");}});Function.implement({extend:function(a){for(var b in a){this[b]=a[b];}return this;},create:function(b){var a=this;
b=b||{};return function(d){var c=b.arguments;c=(c!=undefined)?$splat(c):Array.slice(arguments,(b.event)?1:0);if(b.event){c=[d||window.event].extend(c);
}var e=function(){return a.apply(b.bind||null,c);};if(b.delay){return setTimeout(e,b.delay);}if(b.periodical){return setInterval(e,b.periodical);}if(b.attempt){return $try(e);
}return e();};},run:function(a,b){return this.apply(b,$splat(a));},pass:function(a,b){return this.create({bind:b,arguments:a});},bind:function(b,a){return this.create({bind:b,arguments:a});
},bindWithEvent:function(b,a){return this.create({bind:b,arguments:a,event:true});},attempt:function(a,b){return this.create({bind:b,arguments:a,attempt:true})();
},delay:function(b,c,a){return this.create({bind:c,arguments:a,delay:b})();},periodical:function(c,b,a){return this.create({bind:b,arguments:a,periodical:c})();
}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0);return Math.round(this*a)/a;},times:function(b,c){for(var a=0;
a<this;a++){b.call(c,a,this);}},toFloat:function(){return parseFloat(this);},toInt:function(a){return parseInt(this,a||10);}});Number.alias("times","each");
(function(b){var a={};b.each(function(c){if(!Number[c]){a[c]=function(){return Math[c].apply(null,[this].concat($A(arguments)));};}});Number.implement(a);
})(["abs","acos","asin","atan","atan2","ceil","cos","exp","floor","log","max","min","pow","sin","sqrt","tan"]);String.implement({test:function(a,b){return((typeof a=="string")?new RegExp(a,b):a).test(this);
},contains:function(a,b){return(b)?(b+this+b).indexOf(b+a+b)>-1:this.indexOf(a)>-1;},trim:function(){return this.replace(/^\s+|\s+$/g,"");},clean:function(){return this.replace(/\s+/g," ").trim();
},camelCase:function(){return this.replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();});},hyphenate:function(){return this.replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());
});},capitalize:function(){return this.replace(/\b[a-z]/g,function(a){return a.toUpperCase();});},escapeRegExp:function(){return this.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");
},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);},hexToRgb:function(b){var a=this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=this.match(/\d{1,3}/g);return(a)?a.rgbToHex(b):null;},stripScripts:function(b){var a="";
var c=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(){a+=arguments[1]+"\n";return"";});if(b===true){$exec(a);}else{if($type(b)=="function"){b(a,c);
}}return c;},substitute:function(a,b){return this.replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1);}return(a[c]!=undefined)?a[c]:"";
});}});Hash.implement({has:Object.prototype.hasOwnProperty,keyOf:function(b){for(var a in this){if(this.hasOwnProperty(a)&&this[a]===b){return a;}}return null;
},hasValue:function(a){return(Hash.keyOf(this,a)!==null);},extend:function(a){Hash.each(a,function(c,b){Hash.set(this,b,c);},this);return this;},combine:function(a){Hash.each(a,function(c,b){Hash.include(this,b,c);
},this);return this;},erase:function(a){if(this.hasOwnProperty(a)){delete this[a];}return this;},get:function(a){return(this.hasOwnProperty(a))?this[a]:null;
},set:function(a,b){if(!this[a]||this.hasOwnProperty(a)){this[a]=b;}return this;},empty:function(){Hash.each(this,function(b,a){delete this[a];},this);
return this;},include:function(a,b){if(this[a]==undefined){this[a]=b;}return this;},map:function(b,c){var a=new Hash;Hash.each(this,function(e,d){a.set(d,b.call(c,e,d,this));
},this);return a;},filter:function(b,c){var a=new Hash;Hash.each(this,function(e,d){if(b.call(c,e,d,this)){a.set(d,e);}},this);return a;},every:function(b,c){for(var a in this){if(this.hasOwnProperty(a)&&!b.call(c,this[a],a)){return false;
}}return true;},some:function(b,c){for(var a in this){if(this.hasOwnProperty(a)&&b.call(c,this[a],a)){return true;}}return false;},getKeys:function(){var a=[];
Hash.each(this,function(c,b){a.push(b);});return a;},getValues:function(){var a=[];Hash.each(this,function(b){a.push(b);});return a;},toQueryString:function(a){var b=[];
Hash.each(this,function(f,e){if(a){e=a+"["+e+"]";}var d;switch($type(f)){case"object":d=Hash.toQueryString(f,e);break;case"array":var c={};f.each(function(h,g){c[g]=h;
});d=Hash.toQueryString(c,e);break;default:d=e+"="+encodeURIComponent(f);}if(f!=undefined){b.push(d);}});return b.join("&");}});Hash.alias({keyOf:"indexOf",hasValue:"contains"});
var Event=new Native({name:"Event",initialize:function(a,f){f=f||window;var k=f.document;a=a||f.event;if(a.$extended){return a;}this.$extended=true;var j=a.type;
var g=a.target||a.srcElement;while(g&&g.nodeType==3){g=g.parentNode;}if(j.test(/key/)){var b=a.which||a.keyCode;var m=Event.Keys.keyOf(b);if(j=="keydown"){var d=b-111;
if(d>0&&d<13){m="f"+d;}}m=m||String.fromCharCode(b).toLowerCase();}else{if(j.match(/(click|mouse|menu)/i)){k=(!k.compatMode||k.compatMode=="CSS1Compat")?k.html:k.body;
var i={x:a.pageX||a.clientX+k.scrollLeft,y:a.pageY||a.clientY+k.scrollTop};var c={x:(a.pageX)?a.pageX-f.pageXOffset:a.clientX,y:(a.pageY)?a.pageY-f.pageYOffset:a.clientY};
if(j.match(/DOMMouseScroll|mousewheel/)){var h=(a.wheelDelta)?a.wheelDelta/120:-(a.detail||0)/3;}var e=(a.which==3)||(a.button==2);var l=null;if(j.match(/over|out/)){switch(j){case"mouseover":l=a.relatedTarget||a.fromElement;
break;case"mouseout":l=a.relatedTarget||a.toElement;}if(!(function(){while(l&&l.nodeType==3){l=l.parentNode;}return true;}).create({attempt:Browser.Engine.gecko})()){l=false;
}}}}return $extend(this,{event:a,type:j,page:i,client:c,rightClick:e,wheel:h,relatedTarget:l,target:g,code:b,key:m,shift:a.shiftKey,control:a.ctrlKey,alt:a.altKey,meta:a.metaKey});
}});Event.Keys=new Hash({enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46});Event.implement({stop:function(){return this.stopPropagation().preventDefault();
},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();
}else{this.event.returnValue=false;}return this;}});function Class(b){if(b instanceof Function){b={initialize:b};}var a=function(){Object.reset(this);if(a._prototyping){return this;
}this._current=$empty;var c=(this.initialize)?this.initialize.apply(this,arguments):this;delete this._current;delete this.caller;return c;}.extend(this);
a.implement(b);a.constructor=Class;a.prototype.constructor=a;return a;}Function.prototype.protect=function(){this._protected=true;return this;};Object.reset=function(a,c){if(c==null){for(var e in a){Object.reset(a,e);
}return a;}delete a[c];switch($type(a[c])){case"object":var d=function(){};d.prototype=a[c];var b=new d;a[c]=Object.reset(b);break;case"array":a[c]=$unlink(a[c]);
break;}return a;};new Native({name:"Class",initialize:Class}).extend({instantiate:function(b){b._prototyping=true;var a=new b;delete b._prototyping;return a;
},wrap:function(a,b,c){if(c._origin){c=c._origin;}return function(){if(c._protected&&this._current==null){throw new Error('The method "'+b+'" cannot be called.');
}var e=this.caller,f=this._current;this.caller=f;this._current=arguments.callee;var d=c.apply(this,arguments);this._current=f;this.caller=e;return d;}.extend({_owner:a,_origin:c,_name:b});
}});Class.implement({implement:function(a,d){if($type(a)=="object"){for(var e in a){this.implement(e,a[e]);}return this;}var f=Class.Mutators[a];if(f){d=f.call(this,d);
if(d==null){return this;}}var c=this.prototype;switch($type(d)){case"function":if(d._hidden){return this;}c[a]=Class.wrap(this,a,d);break;case"object":var b=c[a];
if($type(b)=="object"){$mixin(b,d);}else{c[a]=$unlink(d);}break;case"array":c[a]=$unlink(d);break;default:c[a]=d;}return this;}});Class.Mutators={Extends:function(a){this.parent=a;
this.prototype=Class.instantiate(a);this.implement("parent",function(){var b=this.caller._name,c=this.caller._owner.parent.prototype[b];if(!c){throw new Error('The method "'+b+'" has no parent.');
}return c.apply(this,arguments);}.protect());},Implements:function(a){$splat(a).each(function(b){if(b instanceof Function){b=Class.instantiate(b);}this.implement(b);
},this);}};var Chain=new Class({$chain:[],chain:function(){this.$chain.extend(Array.flatten(arguments));return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;
},clearChain:function(){this.$chain.empty();return this;}});var Events=new Class({$events:{},addEvent:function(c,b,a){c=Events.removeOn(c);if(b!=$empty){this.$events[c]=this.$events[c]||[];
this.$events[c].include(b);if(a){b.internal=true;}}return this;},addEvents:function(a){for(var b in a){this.addEvent(b,a[b]);}return this;},fireEvent:function(c,b,a){c=Events.removeOn(c);
if(!this.$events||!this.$events[c]){return this;}this.$events[c].each(function(d){d.create({bind:this,delay:a,"arguments":b})();},this);return this;},removeEvent:function(b,a){b=Events.removeOn(b);
if(!this.$events[b]){return this;}if(!a.internal){this.$events[b].erase(a);}return this;},removeEvents:function(c){var d;if($type(c)=="object"){for(d in c){this.removeEvent(d,c[d]);
}return this;}if(c){c=Events.removeOn(c);}for(d in this.$events){if(c&&c!=d){continue;}var b=this.$events[d];for(var a=b.length;a--;a){this.removeEvent(d,b[a]);
}}return this;}});Events.removeOn=function(a){return a.replace(/^on([A-Z])/,function(b,c){return c.toLowerCase();});};var Options=new Class({setOptions:function(){this.options=$merge.run([this.options].extend(arguments));
if(!this.addEvent){return this;}for(var a in this.options){if($type(this.options[a])!="function"||!(/^on[A-Z]/).test(a)){continue;}this.addEvent(a,this.options[a]);
delete this.options[a];}return this;}});var Element=new Native({name:"Element",legacy:window.Element,initialize:function(a,b){var c=Element.Constructors.get(a);
if(c){return c(b);}if(typeof a=="string"){return document.newElement(a,b);}return $(a).set(b);},afterImplement:function(a,b){Element.Prototype[a]=b;if(Array[a]){return;
}Elements.implement(a,function(){var c=[],g=true;for(var e=0,d=this.length;e<d;e++){var f=this[e][a].apply(this[e],arguments);c.push(f);if(g){g=($type(f)=="element");
}}return(g)?new Elements(c):c;});}});Element.Prototype={$family:{name:"element"}};Element.Constructors=new Hash;var IFrame=new Native({name:"IFrame",generics:false,initialize:function(){var e=Array.link(arguments,{properties:Object.type,iframe:$defined});
var c=e.properties||{};var b=$(e.iframe)||false;var d=c.onload||$empty;delete c.onload;c.id=c.name=$pick(c.id,c.name,b.id,b.name,"IFrame_"+$time());b=new Element(b||"iframe",c);
var a=function(){var f=$try(function(){return b.contentWindow.location.host;});if(f&&f==window.location.host){var g=new Window(b.contentWindow);new Document(b.contentWindow.document);
$extend(g.Element.prototype,Element.Prototype);}d.call(b.contentWindow,b.contentWindow.document);};(window.frames[c.id])?a():b.addListener("load",a);return b;
}});var Elements=new Native({initialize:function(f,b){b=$extend({ddup:true,cash:true},b);f=f||[];if(b.ddup||b.cash){var g={},e=[];for(var c=0,a=f.length;
c<a;c++){var d=$.element(f[c],!b.cash);if(b.ddup){if(g[d.uid]){continue;}g[d.uid]=true;}e.push(d);}f=e;}return(b.cash)?$extend(f,this):f;}});Elements.implement({filter:function(a,b){if(!a){return this;
}return new Elements(Array.filter(this,(typeof a=="string")?function(c){return c.match(a);}:a,b));}});Document.implement({newElement:function(a,b){if(Browser.Engine.trident&&b){["name","type","checked"].each(function(c){if(!b[c]){return;
}a+=" "+c+'="'+b[c]+'"';if(c!="checked"){delete b[c];}});a="<"+a+">";}return $.element(this.createElement(a)).set(b);},newTextNode:function(a){return this.createTextNode(a);
},getDocument:function(){return this;},getWindow:function(){return this.window;}});Window.implement({$:function(b,c){if(b&&b.$family&&b.uid){return b;}var a=$type(b);
return($[a])?$[a](b,c,this.document):null;},$$:function(a){if(arguments.length==1&&typeof a=="string"){return this.document.getElements(a);}var f=[];var c=Array.flatten(arguments);
for(var d=0,b=c.length;d<b;d++){var e=c[d];switch($type(e)){case"element":f.push(e);break;case"string":f.extend(this.document.getElements(e,true));}}return new Elements(f);
},getDocument:function(){return this.document;},getWindow:function(){return this;}});$.string=function(c,b,a){c=a.getElementById(c);return(c)?$.element(c,b):null;
};$.element=function(a,d){$uid(a);if(!d&&!a.$family&&!(/^object|embed$/i).test(a.tagName)){var b=Element.Prototype;for(var c in b){a[c]=b[c];}}return a;
};$.object=function(b,c,a){if(b.toElement){return $.element(b.toElement(a),c);}return null;};$.textnode=$.whitespace=$.window=$.document=$arguments(0);
Native.implement([Element,Document],{getElement:function(a,b){return $(this.getElements(a,true)[0]||null,b);},getElements:function(a,d){a=a.split(",");
var c=[];var b=(a.length>1);a.each(function(e){var f=this.getElementsByTagName(e.trim());(b)?c.extend(f):c=f;},this);return new Elements(c,{ddup:b,cash:!d});
}});(function(){var h={},f={};var i={input:"checked",option:"selected",textarea:(Browser.Engine.webkit&&Browser.Engine.version<420)?"innerHTML":"value"};
var c=function(l){return(f[l]||(f[l]={}));};var g=function(n,l){if(!n){return;}var m=n.uid;if(Browser.Engine.trident){if(n.clearAttributes){var q=l&&n.cloneNode(false);
n.clearAttributes();if(q){n.mergeAttributes(q);}}else{if(n.removeEvents){n.removeEvents();}}if((/object/i).test(n.tagName)){for(var o in n){if(typeof n[o]=="function"){n[o]=$empty;
}}Element.dispose(n);}}if(!m){return;}h[m]=f[m]=null;};var d=function(){Hash.each(h,g);if(Browser.Engine.trident){$A(document.getElementsByTagName("object")).each(g);
}if(window.CollectGarbage){CollectGarbage();}h=f=null;};var j=function(n,l,s,m,p,r){var o=n[s||l];var q=[];while(o){if(o.nodeType==1&&(!m||Element.match(o,m))){if(!p){return $(o,r);
}q.push(o);}o=o[l];}return(p)?new Elements(q,{ddup:false,cash:!r}):null;};var e={html:"innerHTML","class":"className","for":"htmlFor",text:(Browser.Engine.trident||(Browser.Engine.webkit&&Browser.Engine.version<420))?"innerText":"textContent"};
var b=["compact","nowrap","ismap","declare","noshade","checked","disabled","readonly","multiple","selected","noresize","defer"];var k=["value","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","maxLength","readOnly","rowSpan","tabIndex","useMap"];
b=b.associate(b);Hash.extend(e,b);Hash.extend(e,k.associate(k.map(String.toLowerCase)));var a={before:function(m,l){if(l.parentNode){l.parentNode.insertBefore(m,l);
}},after:function(m,l){if(!l.parentNode){return;}var n=l.nextSibling;(n)?l.parentNode.insertBefore(m,n):l.parentNode.appendChild(m);},bottom:function(m,l){l.appendChild(m);
},top:function(m,l){var n=l.firstChild;(n)?l.insertBefore(m,n):l.appendChild(m);}};a.inside=a.bottom;Hash.each(a,function(l,m){m=m.capitalize();Element.implement("inject"+m,function(n){l(this,$(n,true));
return this;});Element.implement("grab"+m,function(n){l($(n,true),this);return this;});});Element.implement({set:function(o,m){switch($type(o)){case"object":for(var n in o){this.set(n,o[n]);
}break;case"string":var l=Element.Properties.get(o);(l&&l.set)?l.set.apply(this,Array.slice(arguments,1)):this.setProperty(o,m);}return this;},get:function(m){var l=Element.Properties.get(m);
return(l&&l.get)?l.get.apply(this,Array.slice(arguments,1)):this.getProperty(m);},erase:function(m){var l=Element.Properties.get(m);(l&&l.erase)?l.erase.apply(this):this.removeProperty(m);
return this;},setProperty:function(m,n){var l=e[m];if(n==undefined){return this.removeProperty(m);}if(l&&b[m]){n=!!n;}(l)?this[l]=n:this.setAttribute(m,""+n);
return this;},setProperties:function(l){for(var m in l){this.setProperty(m,l[m]);}return this;},getProperty:function(m){var l=e[m];var n=(l)?this[l]:this.getAttribute(m,2);
return(b[m])?!!n:(l)?n:n||null;},getProperties:function(){var l=$A(arguments);return l.map(this.getProperty,this).associate(l);},removeProperty:function(m){var l=e[m];
(l)?this[l]=(l&&b[m])?false:"":this.removeAttribute(m);return this;},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this;
},hasClass:function(l){return this.className.contains(l," ");},addClass:function(l){if(!this.hasClass(l)){this.className=(this.className+" "+l).clean();
}return this;},removeClass:function(l){this.className=this.className.replace(new RegExp("(^|\\s)"+l+"(?:\\s|$)"),"$1");return this;},toggleClass:function(l){return this.hasClass(l)?this.removeClass(l):this.addClass(l);
},adopt:function(){Array.flatten(arguments).each(function(l){l=$(l,true);if(l){this.appendChild(l);}},this);return this;},appendText:function(m,l){return this.grab(this.getDocument().newTextNode(m),l);
},grab:function(m,l){a[l||"bottom"]($(m,true),this);return this;},inject:function(m,l){a[l||"bottom"](this,$(m,true));return this;},replaces:function(l){l=$(l,true);
l.parentNode.replaceChild(this,l);return this;},wraps:function(m,l){m=$(m,true);return this.replaces(m).grab(m,l);},getPrevious:function(l,m){return j(this,"previousSibling",null,l,false,m);
},getAllPrevious:function(l,m){return j(this,"previousSibling",null,l,true,m);},getNext:function(l,m){return j(this,"nextSibling",null,l,false,m);},getAllNext:function(l,m){return j(this,"nextSibling",null,l,true,m);
},getFirst:function(l,m){return j(this,"nextSibling","firstChild",l,false,m);},getLast:function(l,m){return j(this,"previousSibling","lastChild",l,false,m);
},getParent:function(l,m){return j(this,"parentNode",null,l,false,m);},getParents:function(l,m){return j(this,"parentNode",null,l,true,m);},getSiblings:function(l,m){return this.getParent().getChildren(l,m).erase(this);
},getChildren:function(l,m){return j(this,"nextSibling","firstChild",l,true,m);},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;
},getElementById:function(o,n){var m=this.ownerDocument.getElementById(o);if(!m){return null;}for(var l=m.parentNode;l!=this;l=l.parentNode){if(!l){return null;
}}return $.element(m,n);},getSelected:function(){return new Elements($A(this.options).filter(function(l){return l.selected;}));},getComputedStyle:function(m){if(this.currentStyle){return this.currentStyle[m.camelCase()];
}var l=this.getDocument().defaultView.getComputedStyle(this,null);return(l)?l.getPropertyValue([m.hyphenate()]):null;},toQueryString:function(){var l=[];
this.getElements("input, select, textarea",true).each(function(m){if(!m.name||m.disabled){return;}var n=(m.tagName.toLowerCase()=="select")?Element.getSelected(m).map(function(o){return o.value;
}):((m.type=="radio"||m.type=="checkbox")&&!m.checked)?null:m.value;$splat(n).each(function(o){if(typeof o!="undefined"){l.push(m.name+"="+encodeURIComponent(o));
}});});return l.join("&");},clone:function(o,l){o=o!==false;var r=this.cloneNode(o);var n=function(v,u){if(!l){v.removeAttribute("id");}if(Browser.Engine.trident){v.clearAttributes();
v.mergeAttributes(u);v.removeAttribute("uid");if(v.options){var w=v.options,s=u.options;for(var t=w.length;t--;){w[t].selected=s[t].selected;}}}var x=i[u.tagName.toLowerCase()];
if(x&&u[x]){v[x]=u[x];}};if(o){var p=r.getElementsByTagName("*"),q=this.getElementsByTagName("*");for(var m=p.length;m--;){n(p[m],q[m]);}}n(r,this);return $(r);
},destroy:function(){Element.empty(this);Element.dispose(this);g(this,true);return null;},empty:function(){$A(this.childNodes).each(function(l){Element.destroy(l);
});return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},hasChild:function(l){l=$(l,true);if(!l){return false;
}if(Browser.Engine.webkit&&Browser.Engine.version<420){return $A(this.getElementsByTagName(l.tagName)).contains(l);}return(this.contains)?(this!=l&&this.contains(l)):!!(this.compareDocumentPosition(l)&16);
},match:function(l){return(!l||(l==this)||(Element.get(this,"tag")==l));}});Native.implement([Element,Window,Document],{addListener:function(o,n){if(o=="unload"){var l=n,m=this;
n=function(){m.removeListener("unload",n);l();};}else{h[this.uid]=this;}if(this.addEventListener){this.addEventListener(o,n,false);}else{this.attachEvent("on"+o,n);
}return this;},removeListener:function(m,l){if(this.removeEventListener){this.removeEventListener(m,l,false);}else{this.detachEvent("on"+m,l);}return this;
},retrieve:function(m,l){var o=c(this.uid),n=o[m];if(l!=undefined&&n==undefined){n=o[m]=l;}return $pick(n);},store:function(m,l){var n=c(this.uid);n[m]=l;
return this;},eliminate:function(l){var m=c(this.uid);delete m[l];return this;}});window.addListener("unload",d);})();Element.Properties=new Hash;Element.Properties.style={set:function(a){this.style.cssText=a;
},get:function(){return this.style.cssText;},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();
}};Element.Properties.html=(function(){var c=document.createElement("div");var a={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
a.thead=a.tfoot=a.tbody;var b={set:function(){var e=Array.flatten(arguments).join("");var f=Browser.Engine.trident&&a[this.get("tag")];if(f){var g=c;g.innerHTML=f[1]+e+f[2];
for(var d=f[0];d--;){g=g.firstChild;}this.empty().adopt(g.childNodes);}else{this.innerHTML=e;}}};b.erase=b.set;return b;})();if(Browser.Engine.webkit&&Browser.Engine.version<420){Element.Properties.text={get:function(){if(this.innerText){return this.innerText;
}var a=this.ownerDocument.newElement("div",{html:this.innerHTML}).inject(this.ownerDocument.body);var b=a.innerText;a.destroy();return b;}};}Element.Properties.events={set:function(a){this.addEvents(a);
}};Native.implement([Element,Window,Document],{addEvent:function(e,g){var h=this.retrieve("events",{});h[e]=h[e]||{keys:[],values:[]};if(h[e].keys.contains(g)){return this;
}h[e].keys.push(g);var f=e,a=Element.Events.get(e),c=g,i=this;if(a){if(a.onAdd){a.onAdd.call(this,g);}if(a.condition){c=function(j){if(a.condition.call(this,j)){return g.call(this,j);
}return true;};}f=a.base||f;}var d=function(){return g.call(i);};var b=Element.NativeEvents[f];if(b){if(b==2){d=function(j){j=new Event(j,i.getWindow());
if(c.call(i,j)===false){j.stop();}};}this.addListener(f,d);}h[e].values.push(d);return this;},removeEvent:function(c,b){var a=this.retrieve("events");if(!a||!a[c]){return this;
}var f=a[c].keys.indexOf(b);if(f==-1){return this;}a[c].keys.splice(f,1);var e=a[c].values.splice(f,1)[0];var d=Element.Events.get(c);if(d){if(d.onRemove){d.onRemove.call(this,b);
}c=d.base||c;}return(Element.NativeEvents[c])?this.removeListener(c,e):this;},addEvents:function(a){for(var b in a){this.addEvent(b,a[b]);}return this;
},removeEvents:function(a){var c;if($type(a)=="object"){for(c in a){this.removeEvent(c,a[c]);}return this;}var b=this.retrieve("events");if(!b){return this;
}if(!a){for(c in b){this.removeEvents(c);}this.eliminate("events");}else{if(b[a]){while(b[a].keys[0]){this.removeEvent(a,b[a].keys[0]);}b[a]=null;}}return this;
},fireEvent:function(d,b,a){var c=this.retrieve("events");if(!c||!c[d]){return this;}c[d].keys.each(function(e){e.create({bind:this,delay:a,"arguments":b})();
},this);return this;},cloneEvents:function(d,a){d=$(d);var c=d.retrieve("events");if(!c){return this;}if(!a){for(var b in c){this.cloneEvents(d,b);}}else{if(c[a]){c[a].keys.each(function(e){this.addEvent(a,e);
},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,load:1,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1};
(function(){var a=function(b){var c=b.relatedTarget;if(c==undefined){return true;}if(c===false){return false;}return($type(this)!="document"&&c!=this&&c.prefix!="xul"&&!this.hasChild(c));
};Element.Events=new Hash({mouseenter:{base:"mouseover",condition:a},mouseleave:{base:"mouseout",condition:a},mousewheel:{base:(Browser.Engine.gecko)?"DOMMouseScroll":"mousewheel"}});
})();Element.Properties.styles={set:function(a){this.setStyles(a);}};Element.Properties.opacity={set:function(a,b){if(!b){if(a==0){if(this.style.visibility!="hidden"){this.style.visibility="hidden";
}}else{if(this.style.visibility!="visible"){this.style.visibility="visible";}}}if(!this.currentStyle||!this.currentStyle.hasLayout){this.style.zoom=1;}if(Browser.Engine.trident){this.style.filter=(a==1)?"":"alpha(opacity="+a*100+")";
}this.style.opacity=a;this.store("opacity",a);},get:function(){return this.retrieve("opacity",1);}};Element.implement({setOpacity:function(a){return this.set("opacity",a,true);
},getOpacity:function(){return this.get("opacity");},setStyle:function(b,a){switch(b){case"opacity":return this.set("opacity",parseFloat(a));case"float":b=(Browser.Engine.trident)?"styleFloat":"cssFloat";
}b=b.camelCase();if($type(a)!="string"){var c=(Element.Styles.get(b)||"@").split(" ");a=$splat(a).map(function(e,d){if(!c[d]){return"";}return($type(e)=="number")?c[d].replace("@",Math.round(e)):e;
}).join(" ");}else{if(a==String(Number(a))){a=Math.round(a);}}this.style[b]=a;return this;},getStyle:function(g){switch(g){case"opacity":return this.get("opacity");
case"float":g=(Browser.Engine.trident)?"styleFloat":"cssFloat";}g=g.camelCase();var a=this.style[g];if(!$chk(a)){a=[];for(var f in Element.ShortStyles){if(g!=f){continue;
}for(var e in Element.ShortStyles[f]){a.push(this.getStyle(e));}return a.join(" ");}a=this.getComputedStyle(g);}if(a){a=String(a);var c=a.match(/rgba?\([\d\s,]+\)/);
if(c){a=a.replace(c[0],c[0].rgbToHex());}}if(Browser.Engine.presto||(Browser.Engine.trident&&!$chk(parseInt(a,10)))){if(g.test(/^(height|width)$/)){var b=(g=="width")?["left","right"]:["top","bottom"],d=0;
b.each(function(h){d+=this.getStyle("border-"+h+"-width").toInt()+this.getStyle("padding-"+h).toInt();},this);return this["offset"+g.capitalize()]-d+"px";
}if((Browser.Engine.presto)&&String(a).test("px")){return a;}if(g.test(/(border(.+)Width|margin|padding)/)){return"0px";}}return a;},setStyles:function(b){for(var a in b){this.setStyle(a,b[a]);
}return this;},getStyles:function(){var a={};Array.each(arguments,function(b){a[b]=this.getStyle(b);},this);return a;}});Element.Styles=new Hash({left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"});
Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(g){var f=Element.ShortStyles;
var b=Element.Styles;["margin","padding"].each(function(h){var i=h+g;f[h][i]=b[i]="@px";});var e="border"+g;f.border[e]=b[e]="@px @ rgb(@, @, @)";var d=e+"Width",a=e+"Style",c=e+"Color";
f[e]={};f.borderWidth[d]=f[e][d]=b[d]="@px";f.borderStyle[a]=f[e][a]=b[a]="@";f.borderColor[c]=f[e][c]=b[c]="rgb(@, @, @)";});Native.implement([Document,Element],{getElements:function(h,g){h=h.split(",");
var c,e={};for(var d=0,b=h.length;d<b;d++){var a=h[d],f=Selectors.Utils.search(this,a,e);if(d!=0&&f.item){f=$A(f);}c=(d==0)?f:(c.item)?$A(c).concat(f):c.concat(f);
}return new Elements(c,{ddup:(h.length>1),cash:!g});}});Element.implement({match:function(b){if(!b||(b==this)){return true;}var d=Selectors.Utils.parseTagAndID(b);
var a=d[0],e=d[1];if(!Selectors.Filters.byID(this,e)||!Selectors.Filters.byTag(this,a)){return false;}var c=Selectors.Utils.parseSelector(b);return(c)?Selectors.Utils.filter(this,c,{}):true;
}});var Selectors={Cache:{nth:{},parsed:{}}};Selectors.RegExps={id:(/#([\w-]+)/),tag:(/^(\w+|\*)/),quick:(/^(\w+|\*)$/),splitter:(/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),combined:(/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)};
Selectors.Utils={chk:function(b,c){if(!c){return true;}var a=$uid(b);if(!c[a]){return c[a]=true;}return false;},parseNthArgument:function(h){if(Selectors.Cache.nth[h]){return Selectors.Cache.nth[h];
}var e=h.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);if(!e){return false;}var g=parseInt(e[1],10);var d=(g||g===0)?g:1;var f=e[2]||false;var c=parseInt(e[3],10)||0;
if(d!=0){c--;while(c<1){c+=d;}while(c>=d){c-=d;}}else{d=c;f="index";}switch(f){case"n":e={a:d,b:c,special:"n"};break;case"odd":e={a:2,b:0,special:"n"};
break;case"even":e={a:2,b:1,special:"n"};break;case"first":e={a:0,special:"index"};break;case"last":e={special:"last-child"};break;case"only":e={special:"only-child"};
break;default:e={a:(d-1),special:"index"};}return Selectors.Cache.nth[h]=e;},parseSelector:function(e){if(Selectors.Cache.parsed[e]){return Selectors.Cache.parsed[e];
}var d,h={classes:[],pseudos:[],attributes:[]};while((d=Selectors.RegExps.combined.exec(e))){var i=d[1],g=d[2],f=d[3],b=d[5],c=d[6],j=d[7];if(i){h.classes.push(i);
}else{if(c){var a=Selectors.Pseudo.get(c);if(a){h.pseudos.push({parser:a,argument:j});}else{h.attributes.push({name:c,operator:"=",value:j});}}else{if(g){h.attributes.push({name:g,operator:f,value:b});
}}}}if(!h.classes.length){delete h.classes;}if(!h.attributes.length){delete h.attributes;}if(!h.pseudos.length){delete h.pseudos;}if(!h.classes&&!h.attributes&&!h.pseudos){h=null;
}return Selectors.Cache.parsed[e]=h;},parseTagAndID:function(b){var a=b.match(Selectors.RegExps.tag);var c=b.match(Selectors.RegExps.id);return[(a)?a[1]:"*",(c)?c[1]:false];
},filter:function(f,c,e){var d;if(c.classes){for(d=c.classes.length;d--;d){var g=c.classes[d];if(!Selectors.Filters.byClass(f,g)){return false;}}}if(c.attributes){for(d=c.attributes.length;
d--;d){var b=c.attributes[d];if(!Selectors.Filters.byAttribute(f,b.name,b.operator,b.value)){return false;}}}if(c.pseudos){for(d=c.pseudos.length;d--;d){var a=c.pseudos[d];
if(!Selectors.Filters.byPseudo(f,a.parser,a.argument,e)){return false;}}}return true;},getByTagAndID:function(b,a,d){if(d){var c=(b.getElementById)?b.getElementById(d,true):Element.getElementById(b,d,true);
return(c&&Selectors.Filters.byTag(c,a))?[c]:[];}else{return b.getElementsByTagName(a);}},search:function(o,h,t){var b=[];var c=h.trim().replace(Selectors.RegExps.splitter,function(k,j,i){b.push(j);
return":)"+i;}).split(":)");var p,e,A;for(var z=0,v=c.length;z<v;z++){var y=c[z];if(z==0&&Selectors.RegExps.quick.test(y)){p=o.getElementsByTagName(y);
continue;}var a=b[z-1];var q=Selectors.Utils.parseTagAndID(y);var B=q[0],r=q[1];if(z==0){p=Selectors.Utils.getByTagAndID(o,B,r);}else{var d={},g=[];for(var x=0,w=p.length;
x<w;x++){g=Selectors.Getters[a](g,p[x],B,r,d);}p=g;}var f=Selectors.Utils.parseSelector(y);if(f){e=[];for(var u=0,s=p.length;u<s;u++){A=p[u];if(Selectors.Utils.filter(A,f,t)){e.push(A);
}}p=e;}}return p;}};Selectors.Getters={" ":function(h,g,j,a,e){var d=Selectors.Utils.getByTagAndID(g,j,a);for(var c=0,b=d.length;c<b;c++){var f=d[c];if(Selectors.Utils.chk(f,e)){h.push(f);
}}return h;},">":function(h,g,j,a,f){var c=Selectors.Utils.getByTagAndID(g,j,a);for(var e=0,d=c.length;e<d;e++){var b=c[e];if(b.parentNode==g&&Selectors.Utils.chk(b,f)){h.push(b);
}}return h;},"+":function(c,b,a,e,d){while((b=b.nextSibling)){if(b.nodeType==1){if(Selectors.Utils.chk(b,d)&&Selectors.Filters.byTag(b,a)&&Selectors.Filters.byID(b,e)){c.push(b);
}break;}}return c;},"~":function(c,b,a,e,d){while((b=b.nextSibling)){if(b.nodeType==1){if(!Selectors.Utils.chk(b,d)){break;}if(Selectors.Filters.byTag(b,a)&&Selectors.Filters.byID(b,e)){c.push(b);
}}}return c;}};Selectors.Filters={byTag:function(b,a){return(a=="*"||(b.tagName&&b.tagName.toLowerCase()==a));},byID:function(a,b){return(!b||(a.id&&a.id==b));
},byClass:function(b,a){return(b.className&&b.className.contains(a," "));},byPseudo:function(a,d,c,b){return d.call(a,c,b);},byAttribute:function(c,d,b,e){var a=Element.prototype.getProperty.call(c,d);
if(!a){return(b=="!=");}if(!b||e==undefined){return true;}switch(b){case"=":return(a==e);case"*=":return(a.contains(e));case"^=":return(a.substr(0,e.length)==e);
case"$=":return(a.substr(a.length-e.length)==e);case"!=":return(a!=e);case"~=":return a.contains(e," ");case"|=":return a.contains(e,"-");}return false;
}};Selectors.Pseudo=new Hash({checked:function(){return this.checked;},empty:function(){return !(this.innerText||this.textContent||"").length;},not:function(a){return !Element.match(this,a);
},contains:function(a){return(this.innerText||this.textContent||"").contains(a);},"first-child":function(){return Selectors.Pseudo.index.call(this,0);},"last-child":function(){var a=this;
while((a=a.nextSibling)){if(a.nodeType==1){return false;}}return true;},"only-child":function(){var b=this;while((b=b.previousSibling)){if(b.nodeType==1){return false;
}}var a=this;while((a=a.nextSibling)){if(a.nodeType==1){return false;}}return true;},"nth-child":function(g,e){g=(g==undefined)?"n":g;var c=Selectors.Utils.parseNthArgument(g);
if(c.special!="n"){return Selectors.Pseudo[c.special].call(this,c.a,e);}var f=0;e.positions=e.positions||{};var d=$uid(this);if(!e.positions[d]){var b=this;
while((b=b.previousSibling)){if(b.nodeType!=1){continue;}f++;var a=e.positions[$uid(b)];if(a!=undefined){f=a+f;break;}}e.positions[d]=f;}return(e.positions[d]%c.a==c.b);
},index:function(a){var b=this,c=0;while((b=b.previousSibling)){if(b.nodeType==1&&++c>a){return false;}}return(c==a);},even:function(b,a){return Selectors.Pseudo["nth-child"].call(this,"2n+1",a);
},odd:function(b,a){return Selectors.Pseudo["nth-child"].call(this,"2n",a);},selected:function(){return this.selected;}});Element.Events.domready={onAdd:function(a){if(Browser.loaded){a.call(this);
}}};(function(){var b=function(){if(Browser.loaded){return;}Browser.loaded=true;window.fireEvent("domready");document.fireEvent("domready");};if(Browser.Engine.trident){var a=document.createElement("div");
(function(){($try(function(){a.doScroll("left");return $(a).inject(document.body).set("html","temp").dispose();}))?b():arguments.callee.delay(50);})();
}else{if(Browser.Engine.webkit&&Browser.Engine.version<525){(function(){(["loaded","complete"].contains(document.readyState))?b():arguments.callee.delay(50);
})();}else{window.addEvent("load",b);document.addEvent("DOMContentLoaded",b);}}})();var JSON=new Hash({$specialChars:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},$replaceChars:function(a){return JSON.$specialChars[a]||"\\u00"+Math.floor(a.charCodeAt()/16).toString(16)+(a.charCodeAt()%16).toString(16);
},encode:function(b){switch($type(b)){case"string":return'"'+b.replace(/[\x00-\x1f\\"]/g,JSON.$replaceChars)+'"';case"array":return"["+String(b.map(JSON.encode).filter($defined))+"]";
case"object":case"hash":var a=[];Hash.each(b,function(e,d){var c=JSON.encode(e);if(c){a.push(JSON.encode(d)+":"+c);}});return"{"+a+"}";case"number":case"boolean":return String(b);
case false:return"null";}return null;},decode:function(string,secure){if($type(string)!="string"||!string.length){return null;}if(secure&&!(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,""))){return null;
}return eval("("+string+")");}});Native.implement([Hash,Array,String,Number],{toJSON:function(){return JSON.encode(this);}});var Cookie=new Class({Implements:Options,options:{path:false,domain:false,duration:false,secure:false,document:document},initialize:function(b,a){this.key=b;
this.setOptions(a);},write:function(b){b=encodeURIComponent(b);if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure";
}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)");
return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,$merge(this.options,{duration:-1})).write("");return this;}});Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);
};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose();};var Fx=new Class({Implements:[Chain,Events,Options],options:{fps:50,unit:false,duration:500,link:"ignore"},initialize:function(a){this.subject=this.subject||this;
this.setOptions(a);this.options.duration=Fx.Durations[this.options.duration]||this.options.duration.toInt();var b=this.options.wait;if(b===false){this.options.link="cancel";
}},getTransition:function(){return function(a){return -(Math.cos(Math.PI*a)-1)/2;};},step:function(){var a=$time();if(a<this.time+this.options.duration){var b=this.transition((a-this.time)/this.options.duration);
this.set(this.compute(this.from,this.to,b));}else{this.set(this.compute(this.from,this.to,1));this.complete();}},set:function(a){return a;},compute:function(c,b,a){return Fx.compute(c,b,a);
},check:function(){if(!this.timer){return true;}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.bind(this,arguments));
return false;}return false;},start:function(b,a){if(!this.check(b,a)){return this;}this.from=b;this.to=a;this.time=0;this.transition=this.getTransition();
this.startTimer();this.onStart();return this;},complete:function(){if(this.stopTimer()){this.onComplete();}return this;},cancel:function(){if(this.stopTimer()){this.onCancel();
}return this;},onStart:function(){this.fireEvent("start",this.subject);},onComplete:function(){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
}},onCancel:function(){this.fireEvent("cancel",this.subject).clearChain();},pause:function(){this.stopTimer();return this;},resume:function(){this.startTimer();
return this;},stopTimer:function(){if(!this.timer){return false;}this.time=$time()-this.time;this.timer=$clear(this.timer);return true;},startTimer:function(){if(this.timer){return false;
}this.time=$time()-this.time;this.timer=this.step.periodical(Math.round(1000/this.options.fps),this);return true;}});Fx.compute=function(c,b,a){return(b-c)*a+c;
};Fx.Durations={"short":250,normal:500,"long":1000};Fx.CSS=new Class({Extends:Fx,prepare:function(d,e,b){b=$splat(b);var c=b[1];if(!$chk(c)){b[1]=b[0];
b[0]=d.getStyle(e);}var a=b.map(this.parse);return{from:a[0],to:a[1]};},parse:function(a){a=$lambda(a)();a=(typeof a=="string")?a.split(" "):$splat(a);
return a.map(function(c){c=String(c);var b=false;Fx.CSS.Parsers.each(function(f,e){if(b){return;}var d=f.parse(c);if($chk(d)){b={value:d,parser:f};}});
b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
});a.$family={name:"fx:css:value"};return a;},serve:function(c,b){if($type(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var b={};Array.each(document.styleSheets,function(e,d){var c=e.href;
if(c&&c.contains("://")&&!c.contains(document.domain)){return;}var f=e.rules||e.cssRules;Array.each(f,function(j,g){if(!j.style){return;}var h=(j.selectorText)?j.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();
}):null;if(!h||!h.test("^"+a+"$")){return;}Element.Styles.each(function(k,i){if(!j.style[i]||Element.ShortStyles[i]){return;}k=String(j.style[i]);b[i]=(k.test(/^rgb/))?k.rgbToHex():k;
});});});return Fx.CSS.Cache[a]=b;}});Fx.CSS.Cache={};Fx.CSS.Parsers=new Hash({Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:$lambda(false),compute:$arguments(1),serve:$arguments(0)}});
Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=$(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
}});Element.Properties.tween={set:function(a){var b=this.retrieve("tween");if(b){b.cancel();}return this.eliminate("tween").store("tween:options",$extend({link:"cancel"},a));
},get:function(a){if(a||!this.retrieve("tween")){if(a||!this.retrieve("tween:options")){this.set("tween",a);}this.store("tween",new Fx.Tween(this,this.retrieve("tween:options")));
}return this.retrieve("tween");}};Element.implement({tween:function(a,c,b){this.get("tween").start(arguments);return this;},fade:function(c){var e=this.get("tween"),d="opacity",a;
c=$pick(c,"toggle");switch(c){case"in":e.start(d,1);break;case"out":e.start(d,0);break;case"show":e.set(d,1);break;case"hide":e.set(d,0);break;case"toggle":var b=this.retrieve("fade:flag",this.get("opacity")==1);
e.start(d,(b)?0:1);this.store("fade:flag",!b);a=true;break;default:e.start(d,arguments);}if(!a){this.eliminate("fade:flag");}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));
a=(a=="transparent")?"#fff":a;}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=$(b);this.parent(a);},set:function(a){if(typeof a=="string"){a=this.search(a);
}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};for(var b in e){a[b]=this.parent(e[b],d[b],c);
}return a;},start:function(b){if(!this.check(b)){return this;}if(typeof b=="string"){b=this.search(b);}var e={},d={};for(var c in b){var a=this.prepare(this.element,c,b[c]);
e[c]=a.from;d[c]=a.to;}return this.parent(e,d);}});Element.Properties.morph={set:function(a){var b=this.retrieve("morph");if(b){b.cancel();}return this.eliminate("morph").store("morph:options",$extend({link:"cancel"},a));
},get:function(a){if(a||!this.retrieve("morph")){if(a||!this.retrieve("morph:options")){this.set("morph",a);}this.store("morph",new Fx.Morph(this,this.retrieve("morph:options")));
}return this.retrieve("morph");}};Element.implement({morph:function(a){this.get("morph").start(a);return this;}});Fx.implement({getTransition:function(){var a=this.options.transition||Fx.Transitions.Sine.easeInOut;
if(typeof a=="string"){var b=a.split(":");a=Fx.Transitions;a=a[b[0]]||a[b[0].capitalize()];if(b[1]){a=a["ease"+b[1].capitalize()+(b[2]?b[2].capitalize():"")];
}}return a;}});Fx.Transition=function(b,a){a=$splat(a);return $extend(b,{easeIn:function(c){return b(c,a);},easeOut:function(c){return 1-b(1-c,a);},easeInOut:function(c){return(c<=0.5)?b(2*c,a)/2:(2-b(2*(1-c),a))/2;
}});};Fx.Transitions=new Hash({linear:$arguments(0)});Fx.Transitions.extend=function(a){for(var b in a){Fx.Transitions[b]=new Fx.Transition(a[b]);}};Fx.Transitions.extend({Pow:function(b,a){return Math.pow(b,a[0]||6);
},Expo:function(a){return Math.pow(2,8*(a-1));},Circ:function(a){return 1-Math.sin(Math.acos(a));},Sine:function(a){return 1-Math.sin((1-a)*Math.PI/2);
},Back:function(b,a){a=a[0]||1.618;return Math.pow(b,2)*((a+1)*b-a);},Bounce:function(f){var e;for(var d=0,c=1;1;d+=c,c/=2){if(f>=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);
break;}}return e;},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,[a+2]);
});});var Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,noCache:false},initialize:function(a){this.xhr=new Browser.Request();
this.setOptions(a);this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.headers=new Hash(this.options.headers);},onStateChange:function(){if(this.xhr.readyState!=4||!this.running){return;
}this.running=false;this.status=0;$try(function(){this.status=this.xhr.status;}.bind(this));if(this.options.isSuccess.call(this,this.status)){this.response={text:this.xhr.responseText,xml:this.xhr.responseXML};
this.success(this.response.text,this.response.xml);}else{this.response={text:null,xml:null};this.failure();}this.xhr.onreadystatechange=$empty;},isSuccess:function(){return((this.status>=200)&&(this.status<300));
},processScripts:function(a){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return $exec(a);}return a.stripScripts(this.options.evalScripts);
},success:function(b,a){this.onSuccess(this.processScripts(b),a);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain();
},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},setHeader:function(a,b){this.headers.set(a,b);
return this;},getHeader:function(a){return $try(function(){return this.xhr.getResponseHeader(a);}.bind(this));},check:function(){if(!this.running){return true;
}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.bind(this,arguments));return false;}return false;},send:function(j){if(!this.check(j)){return this;
}this.running=true;var h=$type(j);if(h=="string"||h=="element"){j={data:j};}var d=this.options;j=$extend({data:d.data,url:d.url,method:d.method},j);var f=j.data,b=j.url,a=j.method;
switch($type(f)){case"element":f=$(f).toQueryString();break;case"object":case"hash":f=Hash.toQueryString(f);}if(this.options.format){var i="format="+this.options.format;
f=(f)?i+"&"+f:i;}if(this.options.emulation&&["put","delete"].contains(a)){var g="_method="+a;f=(f)?g+"&"+f:g;a="post";}if(this.options.urlEncoded&&a=="post"){var c=(this.options.encoding)?"; charset="+this.options.encoding:"";
this.headers.set("Content-type","application/x-www-form-urlencoded"+c);}if(this.options.noCache){var e="noCache="+new Date().getTime();f=(f)?e+"&"+f:e;
}if(f&&a=="get"){b=b+(b.contains("?")?"&":"?")+f;f=null;}this.xhr.open(a.toUpperCase(),b,this.options.async);this.xhr.onreadystatechange=this.onStateChange.bind(this);
this.headers.each(function(l,k){try{this.xhr.setRequestHeader(k,l);}catch(m){this.fireEvent("exception",[k,l]);}},this);this.fireEvent("request");this.xhr.send(f);
if(!this.options.async){this.onStateChange();}return this;},cancel:function(){if(!this.running){return this;}this.running=false;this.xhr.abort();this.xhr.onreadystatechange=$empty;
this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});(function(){var a={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(b){a[b]=function(){var c=Array.link(arguments,{url:String.type,data:$defined});
return this.send($extend(c,{method:b.toLowerCase()}));};});Request.implement(a);})();

317
data/speeddial-head.html Normal file
View file

@ -0,0 +1,317 @@
<!--
Speed Dial head template for Midori.
Copyright (C) 2009 Jean-François Guchens <zcx000@gmail.com>
This file is licensed under the terms of the expat license, see the file EXPAT.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{title}</title>
<script type="text/javascript" src="{res}/mootools.js"></script>
<style>
html, body, #content {
margin: 0px;
padding: 0px
}
body {
text-align: center;
background-color: #fefefe;
font-family: sans-serif;
}
#wrap {
width: 660px;
height: 500px;
margin: 0px auto;
text-align: center;
}
#content {
margin-top: 40px;
}
div.shortcut {
float: left;
border: 1px solid #ccc;
position: relative;
width: 200px;
height: 150px;
margin: 20px 20px 0px 0px;
background-color: #fff;
-webkit-border-radius: 10px;
}
div.right {
margin-right: 0px;
margin-left: 0px;
}
div.top {
margin-top: 0px;
}
h1 {
font-size: 50px;
font-weight: bold;
margin: 0px;
height: 30px;
padding: 10px 0px 0px 0px;
display: block;
}
h4 {
font-size: 11px;
font-weight: bold
margin: 10px 0px 0px 0px;
padding: 10px 5px 0px;
display: block;
}
h4 span:before {
content: "{click_to_add}";
font-size: 11px;
}
h4 span {
font-size: 11px;
}
div.shortcut a {
border: 1px solid #ccc;
display: block;
width: 160px;
height: 107px;
margin: 15px auto 0px;
background-color: #fafafa;
color: #aaa;
text-decoration: none;
}
.waiter img {
margin-top: 38px;
}
div.shortcut a:hover {
border: 1px solid #999;
}
div.shortcut p {
font-size: 12px;
margin: 0px;
padding: 5px 0px 0px;
color: #222;
}
div.clear {
clear: both;
}
div.activated {
background-color: #f5f5f5;
}
.cross {
height: 16px;
width: 16px;
margin-bottom: -17px;
margin-left: 180px;
margin-top: 2px;
background: url({stock}/16/gtk-close) 98% 70% no-repeat;
cursor: pointer;
z-index: -4;
opacity: 0.6;
}
.cross:hover {
opacity: 1;
}
.activated p {
cursor: text;
background: url({stock}/16/gtk-edit) 98% 70% no-repeat;
opacity: 0.6;
color: rgba(0,0,0,1);
}
.activated p:hover {
opacity: 1;
color: rgba(0,0,0,0.5);
}
</style>
<script type="text/javascript">
sc = JSON.decode ({json_data});
var encodeSafe = function (obj) {
var str = JSON.encode (obj);
str = str.replace (/\'/g, '\\\'');
return str;
}
var getAction = function (id)
{
var a = $(id).getFirst ();
if (a.getProperty ('href') != "#" )
return true;
var url = prompt ("{enter_shortcut_address}", "http://");
if (!url) return false;
var name = prompt ("{enter_shortcut_name}", "");
if (!name) name = "";
a.setProperty('href', url);
a.getNext().set('text', name);
var num = id.charAt (1) - 1;
sc.shortcuts[num].title = name;
a.empty();
var im = new Element('img', { src: '{stock}/image-loading' });
a.addClass ('waiter');
a.grab (im);
getThumbnail (id, url);
return false;
}
var getThumbnail = function (id, url)
{
console.log ("speed_dial-get-thumbnail " + id + " " + url);
return false;
}
var setThumbnail = function (id, data, href)
{
var a = $(id).getFirst ();
var im = new Element ('img', { src: 'data:image/png;base64,' + data });
a.empty ().removeClass ('waiter').grab (im);
a.setProperty ('href', href);
var cross = new Element ('div', { 'html': '' });
cross.setProperty ('onclick', 'clearShortcut("' + id + '");');
cross.addClass ('cross');
cross.inject ($(id), 'top');
$(id).addClass ('activated');
var p = a.getNext ();
p.setProperty('onclick', 'javascript:renameShortcut("' + id + '");');
var num = id.charAt (1) - 1;
sc.shortcuts[num].href = href;
sc.shortcuts[num].img = data;
console.log ("speed_dial-save '" + encodeSafe (sc) + "'");
}
var renameShortcut = function (id)
{
var name = prompt ("{enter_shortcut_name}", "");
if (!name) return;
var num = id.charAt (1) - 1;
$(id).getLast ().set ('html', name);
sc.shortcuts[num].title = name;
console.log ("speed_dial-save '" + encodeSafe (sc) + "'");
}
var clearShortcut = function (id)
{
if(!confirm("{are_you_sure}"))
return;
var num = id.charAt (1);
var div = $(id);
var cross = div.getFirst ();
var a = cross.getNext ();
var p = a.getNext ();
cross.dispose ();
div.removeClass ('activated');
a.empty ();
cross.dispose ();
div.removeClass ('activated');
a.empty ().set ('html', '<h1>' + num + '</h1><h4><span/></h4>');
a.setProperty ('href', '#');
p.empty ().removeProperty ('onclick');
num -= 1;
sc.shortcuts[num].href = "#";
sc.shortcuts[num].title = "";
sc.shortcuts[num].img = "";
console.log ("speed_dial-save '" + encodeSafe (sc) + "'");
}
var buildSpeeddial = function ()
{
sc.shortcuts.each (function (item, index, sc)
{
var cl = "shortcut";
if (index == 0 || index == 1 || index == 2)
cl += " top";
if (index == 2 || index == 5 || index == 8)
cl += " right";
var div = new Element ('div', {
'class': cl,
'id': item.id
});
var a = new Element ('a', {
'href': item.href,
'events': {
'click': function () {
return getAction (item.id);
}
}
});
var p = new Element ('p', {
'text': item.title
});
if (item.href == "#")
a.set ('html', '<h1>' + item.id.charAt (1) + '</h1><h4><span/></h4>');
else
{
div.addClass ('activated');
var im = new Element ('img', { src: 'data:image/png;base64,' + item.img });
var cross = new Element ('div', { 'html': '' });
cross.setProperty ('onclick', 'clearShortcut("' + item.id + '");');
cross.addClass ('cross');
cross.inject (div, 'top');
a.grab (im);
p.setProperty('onclick', 'javascript:renameShortcut("' + item.id + '");');
}
div.grab (a);
div.grab (p);
$('content').grab (div);
});
}
window.addEvent ('domready', function () {
buildSpeeddial ();
});
</script>
</head>
<body>
<div id="wrap">
<div id="content">
</div>
</div>
</body>
</html>

1
data/speeddial.json Normal file
View file

@ -0,0 +1 @@
'{"shortcuts":[{"id":"s1","href":"#","title":"","img":""},{"id":"s2","href":"#","title":"","img":""},{"id":"s3","href":"#","title":"","img":""},{"id":"s4","href":"#","title":"","img":""},{"id":"s5","href":"#","title":"","img":""},{"id":"s6","href":"#","title":"","img":""},{"id":"s7","href":"#","title":"","img":""},{"id":"s8","href":"#","title":"","img":""},{"id":"s9","href":"#","title":"","img":""}]}'

385
extensions/adblock.c Normal file
View file

@ -0,0 +1,385 @@
/*
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 <midori/midori.h>
#include <midori/sokoke.h>
#include "config.h"
#include <glib/gstdio.h>
static void
adblock_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension);
static void
adblock_deactivate_cb (MidoriExtension* extension,
GtkWidget* menuitem)
{
MidoriApp* app = midori_extension_get_app (extension);
gtk_widget_destroy (menuitem);
g_signal_handlers_disconnect_by_func (
extension, adblock_deactivate_cb, menuitem);
g_signal_handlers_disconnect_by_func (
app, adblock_app_add_browser_cb, extension);
/* FIXME: Disconnect session callbacks */
}
static void
adblock_preferences_render_text (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
MidoriExtension* extension)
{
gchar* uri;
gtk_tree_model_get (model, iter, 0, &uri, -1);
g_object_set (renderer, "text", uri, NULL);
g_free (uri);
}
static GtkWidget*
adblock_get_preferences_dialog (MidoriExtension* extension)
{
MidoriApp* app;
GtkWidget* browser;
const gchar* dialog_title;
GtkWidget* dialog;
gint width, height;
GtkWidget* xfce_heading;
GtkWidget* hbox;
GtkListStore* liststore;
GtkWidget* treeview;
GtkTreeViewColumn* column;
GtkCellRenderer* renderer_text;
GtkCellRenderer* renderer_pixbuf;
GtkWidget* scrolled;
gchar** filters;
GtkWidget* vbox;
GtkWidget* button;
#if HAVE_OSX
GtkWidget* icon;
#endif
app = midori_extension_get_app (extension);
browser = katze_object_get_object (app, "browser");
dialog_title = _("Configure Advertisement filters");
dialog = gtk_dialog_new_with_buttons (dialog_title, GTK_WINDOW (browser),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
#if !HAVE_OSX
GTK_STOCK_HELP, GTK_RESPONSE_HELP,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
#endif
NULL);
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &dialog);
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES);
/* TODO: Implement some kind of help function */
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
GTK_RESPONSE_HELP, FALSE);
sokoke_widget_get_text_size (dialog, "M", &width, &height);
gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, -1);
g_signal_connect (dialog, "response",
G_CALLBACK (gtk_widget_destroy), dialog);
/* TODO: We need mnemonics */
if ((xfce_heading = sokoke_xfce_header_new (
gtk_window_get_icon_name (GTK_WINDOW (dialog)), dialog_title)))
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
xfce_heading, FALSE, FALSE, 0);
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
TRUE, TRUE, 12);
liststore = gtk_list_store_new (1, G_TYPE_STRING);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
column = gtk_tree_view_column_new ();
renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
/* gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf,
(GtkTreeCellDataFunc)adblock_preferences_render_icon_cb,
treeview, NULL); */
renderer_text = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer_text, TRUE);
gtk_tree_view_column_set_cell_data_func (column, renderer_text,
(GtkTreeCellDataFunc)adblock_preferences_render_text,
extension, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolled), treeview);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 5);
filters = midori_extension_get_string_list (extension, "filters", NULL);
if (filters != NULL)
{
gsize i = 0;
while (filters[i++] != NULL)
gtk_list_store_insert_with_values (GTK_LIST_STORE (liststore),
NULL, i - 1, 0, filters[i -1], -1);
}
g_strfreev (filters);
g_object_unref (liststore);
vbox = gtk_vbox_new (FALSE, 4);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 4);
button = gtk_button_new_from_stock (GTK_STOCK_ADD);
/* g_signal_connect (button, "clicked",
G_CALLBACK (adblock_preferences_add_cb), extension); */
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_set_sensitive (button, FALSE);
button = gtk_button_new_from_stock (GTK_STOCK_EDIT);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_set_sensitive (button, FALSE);
button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
/* g_signal_connect (button, "clicked",
G_CALLBACK (adblock_preferences_remove_cb), extension); */
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_set_sensitive (button, FALSE);
button = gtk_label_new (""); /* This is an invisible separator */
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 8);
gtk_widget_set_sensitive (button, FALSE);
button = gtk_label_new (""); /* This is an invisible separator */
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 12);
button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
#if HAVE_OSX
hbox = gtk_hbox_new (FALSE, 0);
button = gtk_button_new ();
icon = gtk_image_new_from_stock (GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON);
gtk_button_set_image (GTK_BUTTON (button), icon);
/* TODO: Implement some kind of help function */
gtk_widget_set_sensitive (button, FALSE);
/* g_signal_connect (button, "clicked",
G_CALLBACK (adblock_preferences_help_clicked_cb), dialog); */
gtk_box_pack_end (GTK_BOX (hbox),
button, FALSE, FALSE, 4);
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
hbox, FALSE, FALSE, 0);
#endif
gtk_widget_show_all (GTK_DIALOG (dialog)->vbox);
g_object_unref (browser);
return dialog;
}
static void
adblock_menu_configure_filters_activate_cb (GtkWidget* menuitem,
MidoriExtension* extension)
{
static GtkWidget* dialog = NULL;
if (!dialog)
{
dialog = adblock_get_preferences_dialog (extension);
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &dialog);
gtk_widget_show (dialog);
}
else
gtk_window_present (GTK_WINDOW (dialog));
}
static void
adblock_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkWidget* panel;
GtkWidget* menu;
GtkWidget* menuitem;
panel = katze_object_get_object (browser, "panel");
menu = katze_object_get_object (panel, "menu");
menuitem = gtk_menu_item_new_with_mnemonic (_("Configure _Advertisement filters..."));
g_signal_connect (menuitem, "activate",
G_CALLBACK (adblock_menu_configure_filters_activate_cb), extension);
gtk_widget_show (menuitem);
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 3);
g_object_unref (menu);
g_object_unref (panel);
g_signal_connect (extension, "deactivate",
G_CALLBACK (adblock_deactivate_cb), menuitem);
}
static void
adblock_session_request_queued_cb (SoupSession* session,
SoupMessage* msg,
GRegex* regex)
{
SoupURI* soup_uri = soup_message_get_uri (msg);
gchar* uri = soup_uri ? soup_uri_to_string (soup_uri, FALSE) : g_strdup ("");
if (g_regex_match_full (regex, uri, -1, 0, 0, NULL, NULL))
{
/* g_debug ("match! '%s'", uri); */
/* FIXME: This leads to funny error pages if frames are blocked */
soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "adblock", 7);
}
g_free (uri);
}
static void
adblock_session_add_filter (SoupSession* session,
gchar* path)
{
FILE* file;
if ((file = g_fopen (path, "r")))
{
/* We assume filter lists found on the web are commonly very long */
GString* pattern = g_string_sized_new (1000 * 200);
gchar line[255];
GRegex* regex;
GError* error;
while (fgets (line, 255, file))
{
/* Ignore comments and new lines */
if (line[0] == '!')
continue;
/* FIXME: No support for whitelisting */
if (line[0] == '@' && line[1] == '@')
continue;
/* FIXME: Differentiate # comments from element hiding */
/* FIXME: No support for element hiding */
if (line[0] == '#' && line[1] == '#')
continue;
/* FIXME: No support for [include] and [exclude] tags */
if (line[0] == '[')
continue;
g_strchomp (line);
g_string_append (pattern, line);
g_string_append_c (pattern, '|');
}
error = NULL;
if (pattern->len > 2 &&
(regex = g_regex_new (pattern->str, G_REGEX_OPTIMIZE,
G_REGEX_MATCH_NOTEMPTY, &error)))
{
/* g_debug ("%s: '%s'", G_STRFUNC, pattern->str); */
g_signal_connect_data (session, "request-queued",
G_CALLBACK (adblock_session_request_queued_cb),
regex, (GClosureNotify)g_regex_unref, 0);
}
else if (error)
{
/* g_warning ("%s: %s", G_STRFUNC, error->message); */
g_error_free (error);
}
g_string_free (pattern, TRUE);
fclose (file);
}
/* FIXME: This should presumably be freed, but there's a possible crash
g_free (path); */
}
#if WEBKIT_CHECK_VERSION (1, 1, 3)
static void
adblock_download_notify_status_cb (WebKitDownload* download,
GParamSpec* pspec,
gchar* path)
{
SoupSession* session = webkit_get_default_session ();
adblock_session_add_filter (session, path);
/* g_object_unref (download); */
}
#endif
static void
adblock_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
KatzeArray* browsers;
MidoriBrowser* browser;
guint i;
gchar* folder;
gchar** filters;
SoupSession* session;
browsers = katze_object_get_object (app, "browsers");
i = 0;
while ((browser = katze_array_get_nth_item (browsers, i++)))
adblock_app_add_browser_cb (app, browser, extension);
g_signal_connect (app, "add-browser",
G_CALLBACK (adblock_app_add_browser_cb), extension);
g_object_unref (browsers);
session = webkit_get_default_session ();
folder = g_build_filename (g_get_user_cache_dir (), PACKAGE_NAME,
"adblock", NULL);
g_mkdir_with_parents (folder, 0700);
filters = midori_extension_get_string_list (extension, "filters", NULL);
if (filters != NULL)
{
i = 0;
while (filters[i++] != NULL)
{
gchar* filename = g_compute_checksum_for_string (G_CHECKSUM_MD5,
filters[i - 1], -1);
gchar* path = g_build_filename (folder, filename, NULL);
if (!g_file_test (path, G_FILE_TEST_EXISTS))
{
#if WEBKIT_CHECK_VERSION (1, 1, 3)
WebKitNetworkRequest* request;
WebKitDownload* download;
gchar* destination = g_filename_to_uri (path, NULL, NULL);
request = webkit_network_request_new (filters[i -1]);
download = webkit_download_new (request);
g_object_unref (request);
webkit_download_set_destination_uri (download, destination);
g_free (destination);
g_signal_connect (download, "notify::status",
G_CALLBACK (adblock_download_notify_status_cb), path);
webkit_download_start (download);
#else
/* FIXME: Is it worth to rewrite this without WebKitDownload? */
#endif
}
else
adblock_session_add_filter (session, path);
g_free (filename);
}
}
g_strfreev (filters);
g_free (folder);
}
MidoriExtension*
extension_init (void)
{
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Advertisement blocker"),
"description", _("Block advertisements according to a filter list"),
"version", "0.1",
"authors", "Christian Dywan <christian@twotoasts.de>",
NULL);
midori_extension_install_string_list (extension, "filters", NULL, G_MAXSIZE);
g_signal_connect (extension, "activate",
G_CALLBACK (adblock_activate_cb), NULL);
return extension;
}

View file

@ -11,15 +11,6 @@
#include <midori/midori.h>
static void
colorful_tabs_button_toggled_cb (GtkWidget* button,
MidoriExtension* extension)
{
midori_extension_set_boolean (extension, "tint",
!midori_extension_get_boolean (extension, "tint"));
/* FIXME: Update all tab colors */
}
static void
colorful_tabs_view_notify_uri_cb (MidoriView* view,
GParamSpec* pspec,
@ -70,6 +61,25 @@ colorful_tabs_view_notify_uri_cb (MidoriView* view,
}
}
static void
colorful_tabs_browser_foreach_cb (GtkWidget* view,
MidoriExtension* extension)
{
colorful_tabs_view_notify_uri_cb (MIDORI_VIEW (view), NULL, extension);
}
static void
colorful_tabs_button_toggled_cb (GtkWidget* button,
MidoriExtension* extension)
{
MidoriBrowser* browser = MIDORI_BROWSER (gtk_widget_get_toplevel (button));
midori_extension_set_boolean (extension, "tint",
!midori_extension_get_boolean (extension, "tint"));
midori_browser_foreach (browser,
(GtkCallback)colorful_tabs_browser_foreach_cb, extension);
}
static void
colorful_tabs_browser_add_tab_cb (MidoriBrowser* browser,
MidoriView* view,
@ -83,11 +93,12 @@ static void
colorful_tabs_deactivate_cb (MidoriExtension* extension,
GtkWidget* bbox)
{
gtk_widget_destroy (bbox);
g_signal_handlers_disconnect_by_func (
extension, colorful_tabs_deactivate_cb, bbox);
/* FIXME: Disconnect signals */
/* FIXME: Reset all tab colors */
midori_browser_foreach (MIDORI_BROWSER (gtk_widget_get_toplevel (bbox)),
(GtkCallback)colorful_tabs_browser_foreach_cb, extension);
gtk_widget_destroy (bbox);
}
static void

View file

@ -0,0 +1,45 @@
/*
Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)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_MANAGER_PAGE_H__
#define __COOKIE_MANAGER_PAGE_H__
G_BEGIN_DECLS
#define COOKIE_MANAGER_PAGE_TYPE (cookie_manager_page_get_type())
#define COOKIE_MANAGER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
COOKIE_MANAGER_PAGE_TYPE, CookieManagerPage))
#define COOKIE_MANAGER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
COOKIE_MANAGER_PAGE_TYPE, CookieManagerPageClass))
#define IS_COOKIE_MANAGER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
COOKIE_MANAGER_PAGE_TYPE))
#define IS_COOKIE_MANAGER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
COOKIE_MANAGER_PAGE_TYPE))
typedef struct _CookieManagerPage CookieManagerPage;
typedef struct _CookieManagerPageClass CookieManagerPageClass;
struct _CookieManagerPage
{
GtkVBox parent;
};
struct _CookieManagerPageClass
{
GtkVBoxClass parent_class;
};
GType cookie_manager_page_get_type (void);
GtkWidget* cookie_manager_page_new (void);
G_END_DECLS
#endif /* __COOKIE_MANAGER_PAGE_H__ */

View file

@ -0,0 +1,130 @@
/*
Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)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 "config.h"
#include <midori/midori.h>
#include <webkit/webkit.h>
#include "cookie-manager.h"
#include "cookie-manager-page.h"
typedef struct
{
MidoriApp *app;
MidoriBrowser *browser;
MidoriExtension *extension;
GtkWidget *panel_page;
} CMData;
static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext);
static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata);
static void cm_browser_close_cb(GtkObject *browser, CMData *cmdata)
{
g_signal_handlers_disconnect_by_func(cmdata->extension, cm_deactivate_cb, cmdata);
g_signal_handlers_disconnect_by_func(cmdata->browser, cm_browser_close_cb, cmdata);
/* the panel_page widget gets destroyed automatically when a browser is closed but not
* when the extension is deactivated */
if (cmdata->panel_page != NULL && IS_COOKIE_MANAGER_PAGE(cmdata->panel_page))
gtk_widget_destroy(cmdata->panel_page);
g_free(cmdata);
}
static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata)
{
g_signal_handlers_disconnect_by_func(cmdata->app, cm_app_add_browser_cb, extension);
cm_browser_close_cb(NULL, cmdata);
}
static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext)
{
GtkWidget *panel;
GtkWidget *page;
CMData *cmdata;
panel = katze_object_get_object(browser, "panel");
page = cookie_manager_page_new();
gtk_widget_show(page);
midori_panel_append_page(MIDORI_PANEL(panel), MIDORI_VIEWABLE(page));
cmdata = g_new0(CMData, 1);
cmdata->app = app;
cmdata->browser = browser;
cmdata->extension = ext;
cmdata->panel_page = page;
g_signal_connect(browser, "destroy", G_CALLBACK(cm_browser_close_cb), cmdata);
g_signal_connect(ext, "deactivate", G_CALLBACK(cm_deactivate_cb), cmdata);
g_object_unref(panel);
}
static void cm_activate_cb(MidoriExtension *extension, MidoriApp *app, gpointer data)
{
guint i;
KatzeArray *browsers;
MidoriBrowser *browser;
/* add the cookie manager panel page to existing browsers */
browsers = katze_object_get_object(app, "browsers");
i = 0;
while ((browser = katze_array_get_nth_item(browsers, i++)))
cm_app_add_browser_cb(app, browser, extension);
g_object_unref(browsers);
g_signal_connect(app, "add-browser", G_CALLBACK(cm_app_add_browser_cb), extension);
}
MidoriExtension *extension_init(void)
{
MidoriExtension *extension;
GtkIconFactory *factory;
GtkIconSource *icon_source;
GtkIconSet *icon_set;
static GtkStockItem items[] =
{
{ STOCK_COOKIE_MANAGER, N_("_Cookie Manager"), 0, 0, NULL }
};
factory = gtk_icon_factory_new();
gtk_stock_add(items, G_N_ELEMENTS(items));
icon_set = gtk_icon_set_new();
icon_source = gtk_icon_source_new();
gtk_icon_source_set_icon_name(icon_source, GTK_STOCK_DIALOG_AUTHENTICATION);
gtk_icon_set_add_source(icon_set, icon_source);
gtk_icon_source_free(icon_source);
gtk_icon_factory_add(factory, STOCK_COOKIE_MANAGER, icon_set);
gtk_icon_set_unref(icon_set);
gtk_icon_factory_add_default(factory);
g_object_unref(factory);
extension = g_object_new(MIDORI_TYPE_EXTENSION,
"name", _("Cookie Manager"),
"description", _("List, view and delete cookies"),
"version", "0.2",
"authors", "Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>",
NULL);
g_signal_connect(extension, "activate", G_CALLBACK(cm_activate_cb), NULL);
return extension;
}

View file

@ -0,0 +1,20 @@
/*
Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)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_MANAGER_H__
#define __COOKIE_MANAGER_H__
#define STOCK_COOKIE_MANAGER "cookie-manager"
#endif /* __COOKIE_MANAGER_H__ */

View file

@ -0,0 +1,368 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 "feed-atom.h"
#define atom_get_link_attribute(item, attribute) \
(gchar*)g_object_get_data (G_OBJECT (item), attribute)
#define atom_set_link_attribute(item, attribute, value) \
g_object_set_data (G_OBJECT (item), attribute, value)
static gboolean
atom_is_valid (FeedParser* fparser)
{
xmlNodePtr node;
node = fparser->node;
if (!(xmlStrcmp (node->name, BAD_CAST "feed")) &&
!(xmlStrcmp (node->ns->href, BAD_CAST "http://www.w3.org/2005/Atom"))
)
return TRUE;
return FALSE;
}
static gboolean
atom_update (FeedParser* fparser)
{
xmlNodePtr node;
xmlNodePtr child;
gint64 date;
gint64 newdate;
date = katze_item_get_added (fparser->item);
node = fparser->node;
child = node->children;
while (child)
{
if (child->type == XML_ELEMENT_NODE)
{
if (!(xmlStrcmp (child->name, BAD_CAST "updated")))
{
fparser->node = child;
newdate = feed_get_element_date (fparser);
fparser->node = node;
return (date != newdate);
}
}
child = child->next;
}
return TRUE;
}
static gboolean
atom_preferred_link (const gchar* old,
const gchar* new)
{
guint i;
gint iold;
gint inew;
gchar* rels[5] =
{
"enclosure",
"via",
"related",
"alternate",
"self",
};
iold = inew = -1;
for (i = 0; i < 5; i++)
{
if (old && g_str_equal (old, rels[i]))
iold = i;
if (new && g_str_equal (new, rels[i]))
inew = i;
}
return (inew > iold);
}
static void
atom_get_link (KatzeItem* item,
xmlNodePtr node)
{
gchar* oldtype;
gchar* newtype;
gchar* oldrel;
gchar* newrel;
gchar* href;
gboolean oldishtml;
gboolean newishtml;
gboolean newlink;
newlink = FALSE;
oldtype = atom_get_link_attribute (item, "linktype");
oldrel = atom_get_link_attribute (item, "linkrel");
newtype = (gchar*)xmlGetProp (node, BAD_CAST "type");
newrel = (gchar*)xmlGetProp (node, BAD_CAST "rel");
href = (gchar*)xmlGetProp (node, BAD_CAST "href");
if (!newrel)
newrel = g_strdup ("alternate");
oldishtml = (oldtype && g_str_equal (oldtype, "text/html"));
newishtml = (newtype && g_str_equal (newtype, "text/html"));
/* prefer HTML links over anything else.
* if the previous link was already HTML, decide which link
* we prefer.
*/
if ((newishtml && oldishtml) || (!newishtml && !oldishtml))
newlink = atom_preferred_link (oldrel, newrel);
else
newlink = newishtml;
if (newlink)
{
katze_item_set_uri (item, href);
atom_set_link_attribute (item, "linkrel", newrel);
atom_set_link_attribute (item, "linktype", newrel);
}
else
{
xmlFree (href);
xmlFree (newrel);
xmlFree (newtype);
}
}
static gchar*
atom_get_title (FeedParser* fparser)
{
if (!katze_item_get_name (fparser->item))
{
gchar* type;
type = (gchar*)xmlGetProp (fparser->node, BAD_CAST "type");
if (type)
{
gchar* content = NULL;
if (g_str_equal (type, "html") ||
g_str_equal (type, "xhtml"))
content = feed_get_element_markup (fparser);
xmlFree (type);
if (content)
return content;
}
}
return feed_get_element_string (fparser);
}
static void
atom_preparse_entry (FeedParser* fparser)
{
fparser->item = katze_item_new ();
}
static void
atom_parse_entry (FeedParser* fparser)
{
xmlNodePtr node;
gchar* content;
gint64 date;
node = fparser->node;
content = NULL;
if (!xmlStrcmp (node->name, BAD_CAST "id"))
{
content = feed_get_element_string (fparser);
katze_item_set_token (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
{
content = atom_get_title (fparser);
katze_item_set_name (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "summary"))
{
content = feed_get_element_string (fparser);
katze_item_set_text (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "updated"))
{
date = feed_get_element_date (fparser);
katze_item_set_added (fparser->item, date);
}
else if (!xmlStrcmp (node->name, BAD_CAST "icon"))
{
content = feed_get_element_string (fparser);
katze_item_set_icon (fparser->item, content);
}
/* FIXME content can be used in some cases where there
* is no summary, but it needs additional work,
* as it can be HTML, or base64 encoded.
* see the spec.
*/
else if (!xmlStrcmp (node->name, BAD_CAST "content"))
{
/* Only retrieve content if there is no summary */
if (!katze_item_get_text (fparser->item))
{
content = feed_get_element_string (fparser);
katze_item_set_text (fparser->item, content);
}
}
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
atom_get_link (fparser->item, node);
g_free (content);
}
static void
atom_postparse_entry (FeedParser* fparser)
{
if (!*fparser->error)
{
/*
* Verify that the required Atom elements are added
* (as per the spec)
*/
if (!katze_item_get_token (fparser->item) ||
!katze_item_get_name (fparser->item) ||
!katze_item_get_uri (fparser->item) ||
!katze_item_get_added (fparser->item))
{
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find required Atom \"entry\" elements in XML data."));
}
}
if (KATZE_IS_ITEM (fparser->item))
{
atom_set_link_attribute (fparser->item, "linkrel", NULL);
atom_set_link_attribute (fparser->item, "linktype", NULL);
if (*fparser->error)
{
g_object_unref (fparser->item);
fparser->item = NULL;
}
}
}
static void
atom_parse_feed (FeedParser* fparser)
{
FeedParser* eparser;
xmlNodePtr node;
gchar* content;
gint64 date;
node = fparser->node;
content = NULL;
if (!xmlStrcmp (node->name, BAD_CAST "id"))
{
content = feed_get_element_string (fparser);
katze_item_set_token (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
{
content = atom_get_title (fparser);
katze_item_set_name (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "subtitle"))
{
content = feed_get_element_string (fparser);
katze_item_set_text (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "updated"))
{
date = feed_get_element_date (fparser);
katze_item_set_added (fparser->item, date);
}
else if (!xmlStrcmp (node->name, BAD_CAST "icon"))
{
content = feed_get_element_string (fparser);
katze_item_set_icon (fparser->item, content);
}
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
{
atom_get_link (fparser->item, node);
}
else if (!xmlStrcmp (node->name, BAD_CAST "entry"))
{
eparser = g_new0 (FeedParser, 1);
eparser->doc = fparser->doc;
eparser->node = fparser->node;
eparser->error = fparser->error;
eparser->preparse = atom_preparse_entry;
eparser->parse = atom_parse_entry;
eparser->postparse = atom_postparse_entry;
feed_parse_node (eparser);
if (KATZE_IS_ITEM (eparser->item))
{
KatzeItem* item;
if (!(item = feed_item_exists (KATZE_ARRAY (fparser->item), eparser->item)))
katze_array_add_item (KATZE_ARRAY (fparser->item), eparser->item);
else
{
g_object_unref (eparser->item);
katze_array_move_item (KATZE_ARRAY (fparser->item), item, 0);
}
}
g_free (eparser);
}
g_free (content);
}
static void
atom_postparse_feed (FeedParser* fparser)
{
if (KATZE_IS_ARRAY (fparser->item))
{
atom_set_link_attribute (fparser->item, "linkrel", NULL);
atom_set_link_attribute (fparser->item, "linktype", NULL);
}
if (!*fparser->error)
{
/*
* Verify that the required Atom elements are added
* (as per the spec)
*/
if (!katze_item_get_token (fparser->item) ||
!katze_item_get_name (fparser->item) ||
!katze_item_get_added (fparser->item))
{
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find required Atom \"feed\" elements in XML data."));
}
}
}
FeedParser*
atom_init_parser (void)
{
FeedParser* fparser;
fparser = g_new0 (FeedParser, 1);
g_return_val_if_fail (fparser, NULL);
fparser->isvalid = atom_is_valid;
fparser->update = atom_update;
fparser->parse = atom_parse_feed;
fparser->postparse = atom_postparse_feed;
return fparser;
}

View file

@ -0,0 +1,25 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 __FEED_ATOM_H__
#define __FEED_ATOM_H__
#include "feed-parse.h"
G_BEGIN_DECLS
FeedParser*
atom_init_parser (void);
G_END_DECLS
#endif /* __FEED_ATOM_H__ */

View file

@ -0,0 +1,900 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 "feed-panel.h"
#include <midori/midori.h>
#include <midori/sokoke.h>
#include <time.h>
#if HAVE_CONFIG_H
#include <config.h>
#endif
#define STOCK_FEED_PANEL "feed-panel"
struct _FeedPanel
{
GtkVBox parent_instance;
GtkWidget* toolbar;
GtkWidget* treeview;
GtkWidget* webview;
GtkWidget* delete;
GdkPixbuf* pixbuf;
KatzeNet* net;
};
struct _FeedPanelClass
{
GtkVBoxClass parent_class;
};
static void
feed_panel_viewable_iface_init (MidoriViewableIface* iface);
static void
feed_panel_insert_item (FeedPanel* panel,
GtkTreeStore* treestore,
GtkTreeIter* parent,
KatzeItem* item);
static void
feed_panel_disconnect_feed (FeedPanel* panel,
KatzeArray* feed);
G_DEFINE_TYPE_WITH_CODE (FeedPanel, feed_panel, GTK_TYPE_VBOX,
G_IMPLEMENT_INTERFACE (MIDORI_TYPE_VIEWABLE,
feed_panel_viewable_iface_init));
enum
{
ADD_FEED,
REMOVE_FEED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
feed_panel_treeview_render_icon_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
FeedPanel* panel)
{
GdkPixbuf* pixbuf;
KatzeItem* item;
KatzeItem* pitem;
const gchar* uri;
gtk_tree_model_get (model, iter, 0, &item, -1);
g_assert (KATZE_IS_ITEM (item));
if (!KATZE_IS_ARRAY (item))
{
pitem = katze_item_get_parent (item);
g_assert (KATZE_IS_ITEM (pitem));
}
else
pitem = item;
uri = katze_item_get_uri (pitem);
if (uri)
{
pixbuf = katze_net_load_icon (panel->net, uri, NULL, NULL, NULL);
if (!pixbuf)
pixbuf = panel->pixbuf;
}
else
{
pixbuf = gtk_widget_render_icon (panel->treeview,
GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_MENU, NULL);
}
g_object_set (renderer, "pixbuf", pixbuf, NULL);
if (pixbuf != panel->pixbuf)
g_object_unref (pixbuf);
}
static void
feed_panel_treeview_render_text_cb (GtkTreeViewColumn* column,
GtkCellRenderer* renderer,
GtkTreeModel* model,
GtkTreeIter* iter,
GtkWidget* treeview)
{
KatzeItem* item;
const gchar* title;
gtk_tree_model_get (model, iter, 0, &item, -1);
g_assert (KATZE_IS_ITEM (item));
title = katze_item_get_name (item);
if (!title || !*title || g_str_equal (title, " "))
title = katze_item_get_text (item);
if (!title || !*title || g_str_equal (title, " "))
title = katze_item_get_uri (item);
g_object_set (renderer, "text", title, NULL);
g_object_unref (item);
}
static void
feed_panel_add_item_cb (KatzeArray* parent,
KatzeItem* child,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
GtkTreeIter child_iter;
KatzeItem* item;
gint i;
g_return_if_fail (FEED_IS_PANEL (panel));
g_return_if_fail (KATZE_IS_ARRAY (parent));
g_return_if_fail (KATZE_IS_ITEM (child));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
if (katze_item_get_parent (KATZE_ITEM (parent)))
{
if (KATZE_IS_ARRAY (child))
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child_iter,
NULL, G_MAXINT, 0, child, -1);
}
else
{
i = 0;
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i++))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
if (item == KATZE_ITEM (parent))
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child_iter,
&iter, 0, 0, child, -1);
g_object_unref (child);
g_object_unref (item);
break;
}
g_object_unref (item);
}
}
}
feed_panel_insert_item (panel, GTK_TREE_STORE (model), &child_iter, child);
}
static void
feed_panel_remove_iter (GtkTreeModel* model,
KatzeItem* removed_item)
{
guint i;
GtkTreeIter iter;
i = 0;
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
{
KatzeItem* item;
gtk_tree_model_get (model, &iter, 0, &item, -1);
if (item == removed_item)
{
gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
g_object_unref (item);
break;
}
g_object_unref (item);
i++;
}
}
static void
feed_panel_remove_item_cb (KatzeArray* item,
KatzeItem* child,
FeedPanel* panel)
{
GtkTreeModel* model;
KatzeItem* pitem;
g_return_if_fail (FEED_IS_PANEL (panel));
g_return_if_fail (KATZE_IS_ARRAY (item));
g_return_if_fail (KATZE_IS_ITEM (child));
if (KATZE_IS_ARRAY (child))
feed_panel_disconnect_feed (panel, KATZE_ARRAY (child));
if (!katze_item_get_parent (KATZE_ITEM (item)))
{
gint n;
n = katze_array_get_length (KATZE_ARRAY (child));
g_assert (n == 1);
pitem = katze_array_get_nth_item (KATZE_ARRAY (child), 0);
}
else
pitem = child;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
feed_panel_remove_iter (model, pitem);
g_object_unref (pitem);
}
static void
feed_panel_move_item_cb (KatzeArray* feed,
KatzeItem* child,
gint position,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
guint i;
g_return_if_fail (FEED_IS_PANEL (panel));
g_return_if_fail (KATZE_IS_ARRAY (feed));
g_return_if_fail (KATZE_IS_ITEM (child));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
i = 0;
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
{
KatzeItem* item;
gtk_tree_model_get (model, &iter, 0, &item, -1);
if (item == child)
{
gtk_tree_store_move_after (GTK_TREE_STORE (model), &iter, NULL);
g_object_unref (item);
break;
}
g_object_unref (item);
i++;
}
}
static void
feed_panel_disconnect_feed (FeedPanel* panel,
KatzeArray* feed)
{
KatzeItem* item;
guint i;
g_return_if_fail (KATZE_IS_ARRAY (feed));
g_signal_handlers_disconnect_by_func (feed,
feed_panel_add_item_cb, panel);
g_signal_handlers_disconnect_by_func (feed,
feed_panel_remove_item_cb, panel);
g_signal_handlers_disconnect_by_func (feed,
feed_panel_move_item_cb, panel);
i = 0;
while ((item = katze_array_get_nth_item (feed, i++)))
{
if (KATZE_IS_ARRAY (item))
feed_panel_disconnect_feed (panel, KATZE_ARRAY (item));
g_object_unref (item);
}
}
static void
feed_panel_insert_item (FeedPanel* panel,
GtkTreeStore* treestore,
GtkTreeIter* parent,
KatzeItem* item)
{
g_return_if_fail (KATZE_IS_ITEM (item));
if (KATZE_IS_ARRAY (item))
{
g_signal_connect_after (item, "add-item",
G_CALLBACK (feed_panel_add_item_cb), panel);
g_signal_connect_after (item, "move-item",
G_CALLBACK (feed_panel_move_item_cb), panel);
if (!parent)
{
g_signal_connect (item, "remove-item",
G_CALLBACK (feed_panel_remove_item_cb), panel);
}
}
}
static void
feed_panel_row_activated_cb (GtkTreeView* treeview,
GtkTreePath* path,
GtkTreeViewColumn* column,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
const gchar* uri;
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &iter, path))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
uri = katze_item_get_uri (item);
if (uri && *uri)
{
MidoriWebSettings* settings;
MidoriBrowser* browser;
gint n;
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
g_object_unref (item);
}
}
static void
feed_panel_cursor_or_row_changed_cb (GtkTreeView* treeview,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
gboolean sensitive = FALSE;
if (katze_tree_view_get_selected_iter (treeview, &model, &iter))
{
const gchar* uri;
const gchar* text;
gtk_tree_model_get (model, &iter, 0, &item, -1);
uri = katze_item_get_uri (item);
if (KATZE_IS_ARRAY (item))
{
gint64 date;
text = NULL;
if (!uri)
text = g_strdup (katze_item_get_text (KATZE_ITEM (item)));
else
{
KatzeItem* parent;
const gchar* puri;
parent = katze_item_get_parent (item);
g_assert (KATZE_IS_ARRAY (parent));
date = katze_item_get_added (item);
puri = katze_item_get_uri (parent);
if (date)
{
time_t date_t;
const struct tm* tm;
static gchar date_format[512];
gchar* last_updated;
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 */
last_updated = g_strdup_printf (C_("Feed", "Last updated: %s."),
date_format);
text = g_strdup_printf (
"<html><head><title>feed</title></head>"
"<body><h3>%s</h3><p />%s</body></html>",
puri, last_updated);
g_free (last_updated);
}
else
{
text = g_strdup_printf (
"<html><head><title>feed</title></head>"
"<body><h3>%s</h3></body></html>", puri);
}
}
webkit_web_view_load_html_string (
WEBKIT_WEB_VIEW (panel->webview), text ? text : "", uri);
g_free ((gchar*) text);
sensitive = TRUE;
}
else
{
text = katze_item_get_text (item);
webkit_web_view_load_html_string (
WEBKIT_WEB_VIEW (panel->webview), text ? text : "", uri);
}
g_object_unref (item);
}
if (GTK_IS_WIDGET (panel->delete))
gtk_widget_set_sensitive (panel->delete, sensitive);
}
static void
feed_panel_popup_item (GtkWidget* menu,
const gchar* stock_id,
const gchar* label,
KatzeItem* item,
gpointer callback,
FeedPanel* panel)
{
const gchar* uri;
GtkWidget* menuitem;
uri = katze_item_get_uri (item);
menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
if (label)
gtk_label_set_text_with_mnemonic (GTK_LABEL (gtk_bin_get_child (
GTK_BIN (menuitem))), label);
g_object_set_data (G_OBJECT (menuitem), "KatzeItem", item);
g_signal_connect (menuitem, "activate", G_CALLBACK (callback), panel);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
}
static void
feed_panel_open_activate_cb (GtkWidget* menuitem,
FeedPanel* panel)
{
KatzeItem* item;
const gchar* uri;
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
uri = katze_item_get_uri (item);
if (uri && *uri)
{
MidoriBrowser* browser;
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
midori_browser_set_current_uri (browser, uri);
}
}
static void
feed_panel_open_in_tab_activate_cb (GtkWidget* menuitem,
FeedPanel* panel)
{
KatzeItem* item;
const gchar* uri;
guint n;
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
if ((uri = katze_item_get_uri (item)) && *uri)
{
MidoriWebSettings* settings;
MidoriBrowser* browser;
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
}
static void
feed_panel_open_in_window_activate_cb (GtkWidget* menuitem,
FeedPanel* panel)
{
KatzeItem* item;
const gchar* uri;
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
uri = katze_item_get_uri (item);
if (uri && *uri)
{
MidoriBrowser* browser;
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
g_signal_emit_by_name (browser, "new-window", uri);
}
}
static void
feed_panel_delete_activate_cb (GtkWidget* menuitem,
FeedPanel* panel)
{
KatzeItem* item;
g_return_if_fail (FEED_IS_PANEL (panel));
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
g_signal_emit (panel, signals[REMOVE_FEED], 0, item);
}
static void
feed_panel_popup (GtkWidget* widget,
GdkEventButton* event,
KatzeItem* item,
FeedPanel* panel)
{
GtkWidget* menu;
menu = gtk_menu_new ();
if (!KATZE_IS_ARRAY (item))
{
feed_panel_popup_item (menu, GTK_STOCK_OPEN, NULL,
item, feed_panel_open_activate_cb, panel);
feed_panel_popup_item (menu, STOCK_TAB_NEW, _("Open in New _Tab"),
item, feed_panel_open_in_tab_activate_cb, panel);
feed_panel_popup_item (menu, STOCK_WINDOW_NEW, _("Open in New _Window"),
item, feed_panel_open_in_window_activate_cb, panel);
}
else
{
feed_panel_popup_item (menu, GTK_STOCK_DELETE, NULL,
item, feed_panel_delete_activate_cb, panel);
}
sokoke_widget_popup (widget, GTK_MENU (menu),
event, SOKOKE_MENU_POSITION_CURSOR);
}
static gboolean
feed_panel_button_release_event_cb (GtkWidget* widget,
GdkEventButton* event,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
if (event->button != 2 && event->button != 3)
return FALSE;
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
{
KatzeItem* item;
gtk_tree_model_get (model, &iter, 0, &item, -1);
if (event->button == 2)
{
const gchar* uri = katze_item_get_uri (item);
if (uri && *uri)
{
MidoriWebSettings* settings;
MidoriBrowser* browser;
gint n;
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
}
else
feed_panel_popup (widget, event, item, panel);
g_object_unref (item);
return TRUE;
}
return FALSE;
}
static void
feed_panel_popup_menu_cb (GtkWidget* widget,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* item;
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
{
gtk_tree_model_get (model, &iter, 0, &item, -1);
feed_panel_popup (widget, NULL, item, panel);
g_object_unref (item);
}
}
void
feed_panel_add_feeds (FeedPanel* panel,
KatzeItem* feed)
{
GtkTreeModel* model;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
g_assert (GTK_IS_TREE_MODEL (model));
feed_panel_insert_item (panel, GTK_TREE_STORE (model), NULL, feed);
}
static gboolean
webview_button_press_event_cb (GtkWidget* widget,
GdkEventButton* event)
{
/* Disable the popup menu */
return (event->button == 3);
}
static gboolean
webview_navigation_request_cb (WebKitWebView* web_view,
WebKitWebFrame* frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* navigation_action,
WebKitWebPolicyDecision* policy_decision,
FeedPanel* panel)
{
if (webkit_web_navigation_action_get_reason (navigation_action) ==
WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
{
MidoriBrowser* browser;
const gchar* uri;
gint n;
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);
}
return TRUE;
}
static const gchar*
feed_panel_get_label (MidoriViewable* viewable)
{
return _("Feeds");
}
static const gchar*
feed_panel_get_stock_id (MidoriViewable* viewable)
{
return STOCK_FEED_PANEL;
}
static void
feed_panel_add_clicked_cb (GtkWidget* toolitem,
FeedPanel* panel)
{
g_return_if_fail (FEED_IS_PANEL (panel));
g_signal_emit (panel, signals[ADD_FEED], 0);
}
static void
feed_panel_delete_clicked_cb (GtkWidget* toolitem,
FeedPanel* panel)
{
GtkTreeModel* model;
GtkTreeIter iter;
g_return_if_fail (FEED_IS_PANEL (panel));
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (panel->treeview),
&model, &iter))
{
KatzeItem* item;
gtk_tree_model_get (model, &iter, 0, &item, -1);
g_signal_emit (panel, signals[REMOVE_FEED], 0, item);
g_object_unref (item);
}
}
static GtkWidget*
feed_panel_get_toolbar (MidoriViewable* viewable)
{
FeedPanel* panel = FEED_PANEL (viewable);
if (!panel->toolbar)
{
GtkWidget* toolbar;
GtkToolItem* toolitem;
toolbar = gtk_toolbar_new ();
gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
panel->toolbar = toolbar;
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ADD);
gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem), _("Add new feed"));
gtk_tool_item_set_is_important (toolitem, TRUE);
g_signal_connect (toolitem, "clicked",
G_CALLBACK (feed_panel_add_clicked_cb), panel);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
gtk_widget_show (GTK_WIDGET (toolitem));
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_DELETE);
gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem), _("Delete feed"));
g_signal_connect (toolitem, "clicked",
G_CALLBACK (feed_panel_delete_clicked_cb), panel);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
gtk_widget_show (GTK_WIDGET (toolitem));
panel->delete = GTK_WIDGET (toolitem);;
feed_panel_cursor_or_row_changed_cb (
GTK_TREE_VIEW (panel->treeview), panel);
g_signal_connect (panel->delete, "destroy",
G_CALLBACK (gtk_widget_destroyed), &panel->delete);
}
return panel->toolbar;
}
static void
feed_panel_finalize (GObject* object)
{
FeedPanel* panel = FEED_PANEL (object);
g_object_unref (panel->pixbuf);
g_object_unref (panel->net);
}
static void
feed_panel_viewable_iface_init (MidoriViewableIface* iface)
{
iface->get_stock_id = feed_panel_get_stock_id;
iface->get_label = feed_panel_get_label;
iface->get_toolbar = feed_panel_get_toolbar;
}
static void
feed_panel_class_init (FeedPanelClass* class)
{
GObjectClass* gobject_class;
signals[ADD_FEED] = g_signal_new (
"add-feed",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[REMOVE_FEED] = g_signal_new (
"remove-feed",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = feed_panel_finalize;
}
static void
feed_panel_init (FeedPanel* panel)
{
GtkTreeStore* model;
GtkWidget* treewin;
GtkWidget* treeview;
GtkWidget* webview;
GtkWidget* paned;
GtkTreeViewColumn* column;
GtkCellRenderer* renderer_pixbuf;
GtkCellRenderer* renderer_text;
GtkIconFactory *factory;
GtkIconSource *icon_source;
GtkIconSet *icon_set;
WebKitWebSettings* settings;
PangoFontDescription* font_desc;
const gchar* family;
gint size;
GtkStockItem items[] =
{
{ STOCK_FEED_PANEL, N_("_Feeds"), 0, 0, NULL }
};
factory = gtk_icon_factory_new ();
gtk_stock_add (items, G_N_ELEMENTS (items));
icon_set = gtk_icon_set_new ();
icon_source = gtk_icon_source_new ();
gtk_icon_source_set_icon_name (icon_source, STOCK_NEWS_FEED);
gtk_icon_set_add_source (icon_set, icon_source);
gtk_icon_source_free (icon_source);
gtk_icon_factory_add (factory, STOCK_FEED_PANEL, icon_set);
gtk_icon_set_unref (icon_set);
gtk_icon_factory_add_default (factory);
g_object_unref (factory);
panel->net = katze_net_new ();
model = gtk_tree_store_new (1, KATZE_TYPE_ITEM);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
panel->treeview = treeview;
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
column = gtk_tree_view_column_new ();
renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf,
(GtkTreeCellDataFunc)feed_panel_treeview_render_icon_cb,
panel, NULL);
renderer_text = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer_text, FALSE);
gtk_tree_view_column_set_cell_data_func (column, renderer_text,
(GtkTreeCellDataFunc)feed_panel_treeview_render_text_cb,
treeview, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
g_object_unref (model);
g_object_connect (treeview,
"signal::row-activated",
feed_panel_row_activated_cb, panel,
"signal::cursor-changed",
feed_panel_cursor_or_row_changed_cb, panel,
"signal::columns-changed",
feed_panel_cursor_or_row_changed_cb, panel,
"signal::button-release-event",
feed_panel_button_release_event_cb, panel,
"signal::popup-menu",
feed_panel_popup_menu_cb, panel,
NULL);
gtk_widget_show (treeview);
webview = webkit_web_view_new ();
font_desc = treeview->style->font_desc;
family = pango_font_description_get_family (font_desc);
size = pango_font_description_get_size (font_desc) / PANGO_SCALE;
settings = webkit_web_settings_new ();
g_object_set (settings, "default-font-family", family,
"default-font-size", size, NULL);
g_object_set (webview, "settings", settings, NULL);
gtk_widget_set_size_request (webview, -1, 50);
g_object_connect (webview,
"signal::navigation-policy-decision-requested",
webview_navigation_request_cb, panel,
"signal::button-press-event",
webview_button_press_event_cb, NULL,
"signal::button-release-event",
webview_button_press_event_cb, NULL,
NULL);
panel->webview = webview;
treewin = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (treewin),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (treewin),
GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (treewin), treeview);
gtk_widget_show (treewin);
paned = gtk_vpaned_new ();
gtk_paned_pack1 (GTK_PANED (paned), treewin, TRUE, FALSE);
gtk_paned_pack2 (GTK_PANED (paned), webview, TRUE, FALSE);
gtk_box_pack_start (GTK_BOX (panel), paned, TRUE, TRUE, 0);
gtk_widget_show (webview);
gtk_widget_show (paned);
panel->pixbuf = gtk_widget_render_icon (treeview,
STOCK_NEWS_FEED, GTK_ICON_SIZE_MENU, NULL);
}
GtkWidget*
feed_panel_new (void)
{
FeedPanel* panel = g_object_new (FEED_TYPE_PANEL, NULL);
return GTK_WIDGET (panel);
}

View file

@ -0,0 +1,51 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 __FEED_PANEL_H__
#define __FEED_PANEL_H__
#include <midori/midori.h>
G_BEGIN_DECLS
#define FEED_TYPE_PANEL \
(feed_panel_get_type ())
#define FEED_PANEL(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), FEED_TYPE_PANEL, FeedPanel))
#define FEED_PANEL_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), FEED_TYPE_PANEL, FeedPanelClass))
#define FEED_IS_PANEL(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), FEED_TYPE_PANEL))
#define FEED_IS_PANEL_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), FEED_TYPE_PANEL))
#define FEED_PANEL_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), FEED_TYPE_PANEL, FeedPanelClass))
typedef struct _FeedPanel FeedPanel;
typedef struct _FeedPanelClass FeedPanelClass;
void
feed_panel_add_feeds (FeedPanel* panel,
KatzeItem* feed);
void
feed_panel_set_editable (FeedPanel* panel,
gboolean editable);
GType
feed_panel_get_type (void);
GtkWidget*
feed_panel_new (void);
G_END_DECLS
#endif /* __FEED_PANEL_H__ */

View file

@ -0,0 +1,264 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 "feed-parse.h"
#include <time.h>
gchar*
feed_get_element_string (FeedParser* fparser)
{
xmlNodePtr node;
node = fparser->node;
if (!node->children ||
xmlIsBlankNode (node->children) ||
node->children->type != XML_TEXT_NODE
)
{
/* Some servers add required elements with no content,
* create a dummy string to handle it.
*/
return g_strdup (" ");
}
return (gchar*)xmlNodeListGetString (fparser->doc, node->children, 1);
}
static void
handle_markup_chars (void* user_data,
const xmlChar* ch,
int len)
{
if (len > 0)
{
gchar** markup;
gchar* temp;
markup = (gchar**)user_data;
temp = g_strndup ((gchar*)ch, len);
*markup = (*markup) ? g_strconcat (*markup, temp, NULL) : g_strdup (temp);
g_free (temp);
}
}
gchar*
feed_remove_markup (gchar* markup)
{
const xmlChar* stag;
if (((stag = xmlStrchr (BAD_CAST markup, '<')) && xmlStrchr (stag, '>')) ||
xmlStrchr (BAD_CAST markup, '&'))
{
gchar* text = NULL;
htmlSAXHandlerPtr psax;
psax = g_new0 (htmlSAXHandler, 1);
psax->characters = handle_markup_chars;
htmlSAXParseDoc (BAD_CAST markup, "UTF-8", psax, &text);
g_free (psax);
g_free (markup);
return text;
}
return markup;
}
gchar*
feed_get_element_markup (FeedParser* fparser)
{
gchar* markup;
markup = feed_get_element_string (fparser);
return feed_remove_markup (markup);
}
gint64
feed_get_element_date (FeedParser* fparser)
{
time_t date;
gchar* content;
date = 0;
content = feed_get_element_string (fparser);
if (content)
{
SoupDate* sdate;
sdate = soup_date_new_from_string (content);
date = soup_date_to_time_t (sdate);
soup_date_free (sdate);
g_free (content);
}
return ((gint64)date);
}
KatzeItem*
feed_item_exists (KatzeArray* array,
KatzeItem* item)
{
const gchar* guid;
gchar* hstr;
guint hash;
guid = katze_item_get_token (item);
if (!guid)
{
hstr = g_strjoin (NULL,
katze_item_get_name (item),
katze_item_get_uri (item),
katze_item_get_text (item),
NULL);
hash = g_str_hash (hstr);
g_free (hstr);
hstr = g_strdup_printf ("%u", hash);
katze_item_set_token (item, hstr);
g_free (hstr);
guid = katze_item_get_token (item);
}
return (katze_array_find_token (array, guid));
}
void
feed_parse_node (FeedParser* fparser)
{
xmlNodePtr node;
xmlNodePtr child;
if (!*fparser->error)
{
if (fparser->preparse)
(*fparser->preparse) (fparser);
if (fparser->parse)
{
node = fparser->node;
child = node->last;
while (child)
{
if (child->type == XML_ELEMENT_NODE)
{
fparser->node = child;
(*fparser->parse) (fparser);
if (*fparser->error)
break;
}
child = child->prev;
}
fparser->node = node;
}
if (fparser->postparse)
(*fparser->postparse) (fparser);
}
}
static void
feed_parse_doc (xmlDocPtr doc,
GSList* parsers,
KatzeArray* array,
GError** error)
{
FeedParser* fparser;
xmlNodePtr root;
gboolean isvalid;
root = xmlDocGetRootElement (doc);
if (!root)
{
*error = g_error_new (FEED_PARSE_ERROR,
FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find root element in feed XML data."));
return;
}
while (parsers)
{
fparser = (FeedParser*)parsers->data;
fparser->error = error;
fparser->doc = doc;
fparser->node = root;
if (fparser && fparser->isvalid)
{
isvalid = (*fparser->isvalid) (fparser);
if (*fparser->error)
return;
if (isvalid)
{
fparser->item = KATZE_ITEM (array);
if (fparser->update &&
(*fparser->update) (fparser))
feed_parse_node (fparser);
}
}
fparser->error = NULL;
fparser->doc = NULL;
fparser->node = NULL;
if (isvalid)
return;
parsers = g_slist_next (parsers);
}
*error = g_error_new (FEED_PARSE_ERROR,
FEED_PARSE_ERROR_INVALID_FORMAT,
_("Unsupported feed format."));
}
gboolean
parse_feed (gchar* data,
gint64 length,
GSList* parsers,
KatzeArray* array,
GError** error)
{
xmlDocPtr doc;
xmlErrorPtr xerror;
LIBXML_TEST_VERSION
doc = xmlReadMemory (
data, length, "feedfile.xml", NULL,
XML_PARSE_NOWARNING | XML_PARSE_NOERROR /*| XML_PARSE_RECOVER*/
);
if (doc)
{
feed_parse_doc (doc, parsers, array, error);
xmlFreeDoc (doc);
}
else
{
xerror = xmlGetLastError ();
*error = g_error_new (FEED_PARSE_ERROR,
FEED_PARSE_ERROR_PARSE,
_("Failed to parse XML feed: %s"),
xerror->message);
xmlResetLastError ();
}
xmlCleanupParser ();
xmlMemoryDump ();
return *error ? FALSE : TRUE;
}

View file

@ -0,0 +1,85 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 __FEED_PARSE_H__
#define __FEED_PARSE_H__
#include <midori/midori.h>
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <libsoup/soup.h>
#include <libxml/parser.h>
#include <libxml/HTMLparser.h>
G_BEGIN_DECLS
#define FEED_PARSE_ERROR g_quark_from_string("FEED_PARSE_ERROR")
typedef enum
{
FEED_PARSE_ERROR_PARSE,
FEED_PARSE_ERROR_INVALID_FORMAT,
FEED_PARSE_ERROR_INVALID_VERSION,
FEED_PARSE_ERROR_MISSING_ELEMENT
} FeedBarError;
typedef struct _FeedParser
{
xmlDocPtr doc; /* The XML document */
xmlNodePtr node; /* The XML node at a specific point */
KatzeItem* item;
GError** error;
gboolean (*isvalid) (struct _FeedParser* fparser);
gboolean (*update) (struct _FeedParser* fparser);
void (*preparse) (struct _FeedParser* fparser);
void (*parse) (struct _FeedParser* fparser);
void (*postparse) (struct _FeedParser* fparser);
} FeedParser;
#define feed_parser_set_error(fparser, err, msg) \
*(fparser)->error = g_error_new ( \
FEED_PARSE_ERROR, (err), (msg))
gchar*
feed_get_element_string (FeedParser* fparser);
gchar*
feed_remove_markup (gchar* markup);
gchar*
feed_get_element_markup (FeedParser* fparser);
gint64
feed_get_element_date (FeedParser* fparser);
KatzeItem*
feed_item_exists (KatzeArray* array,
KatzeItem* item);
void
feed_parse_node (FeedParser* fparser);
gboolean
parse_feed (gchar* data,
gint64 length,
GSList* parsers,
KatzeArray* array,
GError** error);
G_END_DECLS
#endif /* __FEED_PARSE_H__ */

View file

@ -0,0 +1,268 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 "feed-rss.h"
static gboolean
rss_is_valid (FeedParser* fparser)
{
xmlNodePtr node;
xmlNodePtr child;
xmlChar* str;
gboolean valid;
node = fparser->node;
if (!(xmlStrcmp (node->name, BAD_CAST "rss")))
{
if ((str = xmlGetProp (node, BAD_CAST "version")))
{
valid = !xmlStrcmp (str, BAD_CAST "2.0");
xmlFree (str);
if (valid)
{
child = node->children;
while (child)
{
if (child->type == XML_ELEMENT_NODE &&
!(xmlStrcmp (child->name, BAD_CAST "channel")))
{
fparser->node = child;
return TRUE;
}
child = child->next;
}
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find \"channel\" element in RSS XML data."));
}
else
{
feed_parser_set_error (fparser, FEED_PARSE_ERROR_INVALID_VERSION,
_("Unsupported RSS version found."));
}
}
}
return FALSE;
}
static gboolean
rss_update (FeedParser* fparser)
{
xmlNodePtr node;
xmlNodePtr child;
gint64 date;
gint64 newdate;
date = katze_item_get_added (fparser->item);
node = fparser->node;
child = node->children;
while (child)
{
if (child->type == XML_ELEMENT_NODE)
{
if (!(xmlStrcmp (child->name, BAD_CAST "lastBuildDate")))
{
fparser->node = child;
newdate = feed_get_element_date (fparser);
fparser->node = node;
return (date != newdate);
}
}
child = child->next;
}
return TRUE;
}
static void
rss_preparse_item (FeedParser* fparser)
{
fparser->item = katze_item_new ();
}
static void
rss_parse_item (FeedParser* fparser)
{
xmlNodePtr node;
gchar* content;
gint64 date;
node = fparser->node;
content = NULL;
if (!xmlStrcmp (node->name, BAD_CAST "guid"))
{
content = feed_get_element_string (fparser);
katze_item_set_token (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
{
content = feed_get_element_markup (fparser);
katze_item_set_name (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "description"))
{
content = feed_get_element_string (fparser);
katze_item_set_text (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "pubDate"))
{
date = feed_get_element_date (fparser);
katze_item_set_added (fparser->item, date);
}
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
{
content = feed_get_element_string (fparser);
katze_item_set_uri (fparser->item, content);
}
g_free (content);
}
static void
rss_postparse_item (FeedParser* fparser)
{
if (!*fparser->error)
{
/*
* Verify that the required RSS elements are added
* (as per the spec)
*/
if (!katze_item_get_name (fparser->item))
{
gchar* desc;
desc = (gchar*)katze_item_get_text (fparser->item);
if (!desc)
{
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find required RSS \"item\" elements in XML data."));
}
else
{
desc = feed_remove_markup (g_strdup (desc));
if (desc)
{
katze_item_set_name (fparser->item, desc);
g_free (desc);
}
else
{
if ((desc = (gchar*)katze_item_get_uri (fparser->item)))
katze_item_set_name (fparser->item, desc);
}
}
}
}
if (*fparser->error && KATZE_IS_ITEM (fparser->item))
{
g_object_unref (fparser->item);
fparser->item = NULL;
}
}
static void
rss_parse_channel (FeedParser* fparser)
{
FeedParser* eparser;
xmlNodePtr node;
gchar* content;
gint64 date;
node = fparser->node;
content = NULL;
if (!xmlStrcmp (node->name, BAD_CAST "title"))
{
content = feed_get_element_markup (fparser);
katze_item_set_name (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "description"))
{
content = feed_get_element_string (fparser);
katze_item_set_text (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "lastBuildDate"))
{
date = feed_get_element_date (fparser);
katze_item_set_added (fparser->item, date);
}
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
{
content = feed_get_element_string (fparser);
katze_item_set_uri (fparser->item, content);
}
else if (!xmlStrcmp (node->name, BAD_CAST "item"))
{
eparser = g_new0 (FeedParser, 1);
eparser->doc = fparser->doc;
eparser->node = fparser->node;
eparser->error = fparser->error;
eparser->preparse = rss_preparse_item;
eparser->parse = rss_parse_item;
eparser->postparse = rss_postparse_item;
feed_parse_node (eparser);
if (KATZE_IS_ITEM (eparser->item))
{
KatzeItem* item;
if (!(item = feed_item_exists (KATZE_ARRAY (fparser->item), eparser->item)))
katze_array_add_item (KATZE_ARRAY (fparser->item), eparser->item);
else
{
g_object_unref (eparser->item);
katze_array_move_item (KATZE_ARRAY (fparser->item), item, 0);
}
}
g_free (eparser);
}
g_free (content);
}
static void
rss_postparse_channel (FeedParser* fparser)
{
if (!*fparser->error)
{
/*
* Verify that the required RSS elements are added
* (as per the spec)
*/
if (!katze_item_get_name (fparser->item) ||
!katze_item_get_text (fparser->item) ||
!katze_item_get_uri (fparser->item))
{
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
_("Failed to find required RSS \"channel\" elements in XML data."));
}
}
}
FeedParser*
rss_init_parser (void)
{
FeedParser* fparser;
fparser = g_new0 (FeedParser, 1);
g_return_val_if_fail (fparser, NULL);
fparser->isvalid = rss_is_valid;
fparser->update = rss_update;
fparser->parse = rss_parse_channel;
fparser->postparse = rss_postparse_channel;
return fparser;
}

View file

@ -0,0 +1,25 @@
/*
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
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 __FEED_RSS_H__
#define __FEED_RSS_H__
#include "feed-parse.h"
G_BEGIN_DECLS
FeedParser*
rss_init_parser (void);
G_END_DECLS
#endif /* __FEED_RSS_H__ */

View file

@ -0,0 +1,502 @@
/*
Copyright (C) 2009 Dale Whittaker <dale@users.sf.net>
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 "feed-panel.h"
#include "feed-atom.h"
#include "feed-rss.h"
#include <midori/midori.h>
#define EXTENSION_NAME "Feed Panel"
#define UPDATE_FREQ 10
#define feed_get_flags(feed) \
GPOINTER_TO_INT (g_object_get_data (G_OBJECT ((feed)), "flags"))
#define feed_set_flags(feed, flags) \
g_object_set_data (G_OBJECT ((feed)), "flags", \
GINT_TO_POINTER ((flags)))
#define feed_has_flags(feed, flags) \
((flags) & feed_get_flags ((feed)))
#define feed_add_flags(feed, flags) \
feed_set_flags ((feed), (feed_get_flags ((feed)) | (flags)))
#define feed_remove_flags(feed, flags) \
feed_set_flags ((feed), (feed_get_flags ((feed)) & ~(flags)))
typedef struct
{
MidoriBrowser* browser;
MidoriExtension* extension;
GtkWidget* panel;
KatzeArray* feeds;
KatzeNet* net;
GSList* parsers;
guint source_id;
gboolean is_running;
} FeedPrivate;
typedef struct
{
MidoriExtension* extension;
GSList* parsers;
KatzeArray* feed;
} FeedNetPrivate;
enum
{
FEED_READ,
FEED_REMOVE
};
static void
feed_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension);
static void
feed_deactivate_cb (MidoriExtension* extension,
FeedPrivate* priv)
{
if (priv)
{
MidoriApp* app = midori_extension_get_app (extension);
g_signal_handlers_disconnect_by_func (app,
feed_app_add_browser_cb, extension);
g_signal_handlers_disconnect_by_func (extension,
feed_deactivate_cb, priv);
if (priv->source_id)
g_source_remove (priv->source_id);
g_slist_foreach (priv->parsers, (GFunc)g_free, NULL);
g_slist_free (priv->parsers);
if (priv->feeds)
g_object_unref (priv->net);
if (priv->feeds)
g_object_unref (priv->feeds);
gtk_widget_destroy (priv->panel);
g_free (priv);
}
}
static KatzeArray*
feed_add_item (KatzeArray* feeds,
const gchar* uri)
{
KatzeArray* feed;
feed = NULL;
if (uri)
{
if (katze_array_find_token (feeds, uri))
{
GtkWidget* dialog;
dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
_("Error"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("Feed '%s' already exists"), uri);
gtk_window_set_title (GTK_WINDOW (dialog), EXTENSION_NAME);
gtk_widget_show (dialog);
g_signal_connect_swapped (dialog, "response",
G_CALLBACK (gtk_widget_destroy), dialog);
}
else
{
KatzeArray* child;
feed = katze_array_new (KATZE_TYPE_ARRAY);
child = katze_array_new (KATZE_TYPE_ITEM);
katze_item_set_uri (KATZE_ITEM (feed), uri);
katze_item_set_token (KATZE_ITEM (feed), uri);
katze_item_set_uri (KATZE_ITEM (child), uri);
katze_array_add_item (feeds, feed);
katze_array_add_item (feed, child);
}
}
return feed;
}
static void
feed_save_items (MidoriExtension* extension,
KatzeArray* feed)
{
KatzeItem* item;
gchar** sfeeds;
gint i;
gint n;
g_return_if_fail (KATZE_IS_ARRAY (feed));
n = katze_array_get_length (feed);
sfeeds = g_new (gchar*, n + 1);
for (i = 0; i < n; i++)
{
item = katze_array_get_nth_item (feed, i);
sfeeds[i] = (gchar*) katze_item_get_uri (KATZE_ITEM (item));
}
sfeeds[n] = NULL;
midori_extension_set_string_list (extension, "feeds", sfeeds, n);
g_free (sfeeds);
}
static void
feed_handle_net_error (FeedNetPrivate* netpriv,
const gchar* msg)
{
KatzeItem* child;
const gchar* uri;
gint n;
n = katze_array_get_length (netpriv->feed);
g_assert (n == 1);
child = katze_array_get_nth_item (netpriv->feed, 0);
g_assert (KATZE_IS_ARRAY (child));
uri = katze_item_get_uri (KATZE_ITEM (netpriv->feed));
katze_item_set_name (child, uri);
katze_item_set_text (child, msg);
katze_item_set_uri (child, NULL);
feed_remove_flags (netpriv->feed, FEED_READ);
}
static gboolean
feed_status_cb (KatzeNetRequest* request,
FeedNetPrivate* netpriv)
{
if (request->status == KATZE_NET_FAILED ||
request->status == KATZE_NET_NOT_FOUND)
{
gchar* msg;
msg = g_strdup_printf (_("Error loading feed '%s'"),
katze_item_get_uri (KATZE_ITEM (netpriv->feed)));
feed_handle_net_error (netpriv, msg);
g_free (msg);
return FALSE;
}
return TRUE;
}
static void
feed_transfer_cb (KatzeNetRequest* request,
FeedNetPrivate* netpriv)
{
GError* error;
if (request->status == KATZE_NET_MOVED)
return;
g_return_if_fail (KATZE_IS_ARRAY (netpriv->feed));
error = NULL;
if (request->data)
{
KatzeArray* item;
const gchar* uri;
gint n;
n = katze_array_get_length (netpriv->feed);
g_assert (n == 1);
item = katze_array_get_nth_item (netpriv->feed, 0);
g_assert (KATZE_IS_ARRAY (item));
uri = katze_item_get_uri (KATZE_ITEM (netpriv->feed));
katze_item_set_uri (KATZE_ITEM (item), uri);
if (!parse_feed (request->data, request->length,
netpriv->parsers, item, &error))
{
feed_handle_net_error (netpriv, error->message);
g_error_free (error);
}
if (feed_has_flags (netpriv->feed, FEED_REMOVE))
{
KatzeArray* parent;
/* deferred remove */
parent = katze_item_get_parent (KATZE_ITEM (netpriv->feed));
katze_array_remove_item (parent, netpriv->feed);
feed_save_items (netpriv->extension, parent);
}
else
feed_set_flags (netpriv->feed, 0);
}
netpriv->parsers = NULL;
netpriv->feed = NULL;
g_free (netpriv);
}
static void
update_feed (FeedPrivate* priv,
KatzeItem* feed)
{
if (!(feed_has_flags (feed, FEED_READ)))
{
FeedNetPrivate* netpriv;
feed_add_flags (feed, FEED_READ);
netpriv = g_new0 (FeedNetPrivate, 1);
netpriv->parsers = priv->parsers;
netpriv->extension = priv->extension;
netpriv->feed = KATZE_ARRAY (feed);
katze_net_load_uri (priv->net,
katze_item_get_uri (feed),
(KatzeNetStatusCb) feed_status_cb,
(KatzeNetTransferCb) feed_transfer_cb,
netpriv);
}
}
static gboolean
update_feeds (FeedPrivate* priv)
{
KatzeItem* feed;
gint i;
gint n;
if (!priv->is_running)
{
priv->is_running = TRUE;
n = katze_array_get_length (priv->feeds);
for (i = 0; i < n; i++)
{
feed = katze_array_get_nth_item (priv->feeds, i);
update_feed (priv, feed);
}
}
priv->is_running = FALSE;
return TRUE;
}
static void
secondary_icon_released_cb (GtkAction* action,
GtkWidget* widget,
FeedPrivate* priv)
{
const gchar* uri;
g_assert (KATZE_IS_ARRAY (priv->feeds));
uri = midori_location_action_get_uri (MIDORI_LOCATION_ACTION (action));
if (uri && *uri)
{
KatzeArray* feed;
feed = feed_add_item (priv->feeds, uri);
if (feed)
{
feed_save_items (priv->extension, priv->feeds);
update_feed (priv, KATZE_ITEM (feed));
}
}
}
static void
panel_add_feed_cb (FeedPanel* panel,
FeedPrivate* priv)
{
GtkWidget* dialog;
GtkSizeGroup* sizegroup;
GtkWidget* hbox;
GtkWidget* label;
GtkWidget* entry;
dialog = gtk_dialog_new_with_buttons (
_("New feed"), GTK_WINDOW (priv->browser),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_ADD);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 5);
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
hbox = gtk_hbox_new (FALSE, 8);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
label = gtk_label_new_with_mnemonic (_("_Address:"));
gtk_size_group_add_widget (sizegroup, label);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
entry = gtk_entry_new ();
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_entry_set_text (GTK_ENTRY (entry), "");
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
gtk_widget_show_all (hbox);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
const gchar* uri;
g_assert (KATZE_IS_ARRAY (priv->feeds));
uri = gtk_entry_get_text (GTK_ENTRY (entry));
if (uri && *uri)
{
KatzeArray* feed;
feed = feed_add_item (priv->feeds, uri);
if (feed)
{
feed_save_items (priv->extension, priv->feeds);
update_feed (priv, KATZE_ITEM (feed));
}
}
}
gtk_widget_destroy (dialog);
}
static void
panel_remove_feed_cb (FeedPanel* panel,
KatzeItem* item,
FeedPrivate* priv)
{
KatzeArray* feed;
feed = katze_item_get_parent (item);
g_assert (KATZE_IS_ARRAY (priv->feeds));
g_assert (KATZE_IS_ARRAY (feed));
if (feed_has_flags (feed, FEED_READ))
feed_add_flags (feed, FEED_REMOVE);
else
{
feed_add_flags (feed, FEED_READ);
katze_array_remove_item (priv->feeds, feed);
feed_save_items (priv->extension, priv->feeds);
}
}
static void
feed_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkWidget* panel;
GtkWidget* addon;
GtkActionGroup* action_group;
GtkAction* action;
KatzeNet* net;
KatzeArray* feeds;
KatzeArray* feed;
FeedPrivate* priv;
gchar** sfeeds;
gsize i;
gsize n;
priv = g_new0 (FeedPrivate, 1);
panel = katze_object_get_object (browser, "panel");
addon = feed_panel_new ();
gtk_widget_show (addon);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
g_object_unref (panel);
net = katze_net_new ();
feeds = katze_array_new (KATZE_TYPE_ARRAY);
feed_panel_add_feeds (FEED_PANEL (addon), KATZE_ITEM (feeds));
priv->extension = extension;
priv->browser = browser;
priv->panel = addon;
priv->net = net;
priv->feeds = feeds;
priv->parsers = g_slist_prepend (priv->parsers, atom_init_parser ());
priv->parsers = g_slist_prepend (priv->parsers, rss_init_parser ());
sfeeds = midori_extension_get_string_list (extension, "feeds", &n);
g_assert (n == 0 || sfeeds);
for (i = 0; i < n; i++)
{
if (sfeeds[i])
{
feed = feed_add_item (feeds, sfeeds[i]);
if (feed)
update_feed (priv, KATZE_ITEM (feed));
}
}
g_strdupv (sfeeds);
action_group = midori_browser_get_action_group (browser);
action = gtk_action_group_get_action (action_group, "Location");
g_signal_connect (addon, "add-feed",
G_CALLBACK (panel_add_feed_cb), priv);
g_signal_connect (addon, "remove-feed",
G_CALLBACK (panel_remove_feed_cb), priv);
g_signal_connect (action, "secondary-icon-released",
G_CALLBACK (secondary_icon_released_cb), priv);
g_signal_connect (extension, "deactivate",
G_CALLBACK (feed_deactivate_cb), priv);
priv->source_id = g_timeout_add_seconds (UPDATE_FREQ * 60,
(GSourceFunc) update_feeds, priv);
}
static void
feed_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
KatzeArray* browsers;
MidoriBrowser* browser;
guint i;
browsers = katze_object_get_object (app, "browsers");
i = 0;
while ((browser = katze_array_get_nth_item (browsers, i++)))
feed_app_add_browser_cb (app, browser, extension);
g_object_unref (browsers);
g_signal_connect (app, "add-browser",
G_CALLBACK (feed_app_add_browser_cb), extension);
}
MidoriExtension*
extension_init (void)
{
MidoriExtension* extension;
gchar* sfeed[2];
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Feed Panel"),
"description", _("Read Atom/ RSS feeds"),
"version", "0.1",
"authors", "Dale Whittaker <dayul@users.sf.net>",
NULL);
sfeed[0] = NULL;
midori_extension_install_string_list (extension, "feeds", sfeed, 1);
g_signal_connect (extension, "activate",
G_CALLBACK (feed_activate_cb), NULL);
return extension;
}

View file

@ -13,217 +13,241 @@
#include "mouse-gestures.h"
#define MOUSE_GESTURES_VERSION "0.1"
#define DEVIANCE 20 // the deviance to determine if a line is straight or not
#define MINLENGTH 50 // the minimal length of a line to be treated as a gesture
#define DEVIANCE 20
#define MINLENGTH 50
// #define __MOUSE_GESTURES_DEBUG__ // uncomment for debugging purposes
/* #define __MOUSE_GESTURES_DEBUG__ */
MouseGesture *gesture;
void mouse_gesture_clear(MouseGesture *g)
void mouse_gesture_clear (MouseGesture *g)
{
g->start.x = 0;
g->start.y = 0;
g->middle.x = 0;
g->middle.y = 0;
g->end.x = 0;
g->end.y = 0;
g->last = MOUSE_BUTTON_UNSET;
return;
g->start.x = 0;
g->start.y = 0;
g->middle.x = 0;
g->middle.y = 0;
g->end.x = 0;
g->end.y = 0;
g->last = MOUSE_BUTTON_UNSET;
}
MouseGesture* mouse_gesture_new(void)
MouseGesture* mouse_gesture_new (void)
{
MouseGesture *g = NULL;
if((g = g_new(MouseGesture, 1)) == NULL)
return(NULL);
mouse_gesture_clear(g);
return(g);
MouseGesture *g = g_new (MouseGesture, 1);
mouse_gesture_clear (g);
return g;
}
static gboolean mouse_gestures_handle_events(GtkWidget *widget, GdkEvent *event, MidoriBrowser *browser)
static gboolean mouse_gestures_handle_events (GtkWidget *widget,
GdkEvent *event,
MidoriBrowser *browser)
{
if(event->type == GDK_BUTTON_PRESS) // a button was pressed
{
if(gesture->last == MOUSE_BUTTON_UNSET) // if the gesture was previously cleaned -> new gesture -> new start coordinates
/* A button was pressed */
if (event->type == GDK_BUTTON_PRESS)
{
/* If the gesture was previously cleaned, start a new gesture and coordinates */
if (gesture->last == MOUSE_BUTTON_UNSET)
{
gesture->start.x = event->button.x;
gesture->start.y = event->button.y;
gesture->last = event->button.button;
}
return TRUE;
}
else if (event->type == GDK_MOTION_NOTIFY)
{
if (gesture->last != MOUSE_BUTTON_UNSET)
{
guint x, y;
x = event->motion.x;
y = event->motion.y;
if ((gesture->start.x - x < DEVIANCE && gesture->start.x - x > -DEVIANCE) ||
(gesture->start.y - y < DEVIANCE && gesture->start.y - y > -DEVIANCE))
{
gesture->middle.x = x;
gesture->middle.y = y;
}
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->end.y = y;
}
}
return TRUE;
}
else if (event->type == GDK_BUTTON_RELEASE)
{
/* All mouse gestures will use the middle mouse button */
if (gesture->last == MOUSE_BUTTON_MIDDLE)
{
/* The initial horizontal move is between the bounds */
if ((gesture->middle.x - gesture->start.x < DEVIANCE) &&
(gesture->middle.x - gesture->start.x > -DEVIANCE))
{
/* We initially moved down more than MINLENGTH pixels */
if (gesture->middle.y > gesture->start.y + MINLENGTH)
{
gesture->start.x = event->button.x;
gesture->start.y = event->button.y;
gesture->last = event->button.button;
}
return(TRUE);
}
else if(event->type == GDK_MOTION_NOTIFY) // the mouse was moved
{
if(gesture->last != MOUSE_BUTTON_UNSET)
/* 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 */
midori_browser_activate_action (browser, "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 */
midori_browser_activate_action (browser, "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 */
midori_browser_activate_action (browser, "TabNew");
}
}
/* We initially moved up more than MINLENGTH pixels */
else if (gesture->middle.y + MINLENGTH < gesture->start.y)
{
guint x, y;
x = event->motion.x;
y = event->motion.y;
if((gesture->start.x - x < DEVIANCE && gesture->start.x - x > -DEVIANCE) ||
(gesture->start.y - y < DEVIANCE && gesture->start.y - y > -DEVIANCE))
{
gesture->middle.x = x;
gesture->middle.y = y;
}
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->end.y = y;
}
}
return(TRUE);
}
else if(event->type == GDK_BUTTON_RELEASE)
{
if(gesture->last == MOUSE_BUTTON_MIDDLE) // all mouse gestures will use the middle mouse button
/* The end node was never updated, we only did a vertical move */
if (gesture->end.y == 0 && gesture->end.x == 0)
{
/* We moved up: stop */
midori_browser_activate_action (browser, "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 */
midori_browser_activate_action (browser, "Forward");
}
}
/* We initially moved left more than MINLENGTH pixels */
else if (gesture->middle.x + MINLENGTH < gesture->start.x)
{
// middle mouse button has been released
if(gesture->middle.x - gesture->start.x < DEVIANCE && gesture->middle.x - gesture->start.x > -DEVIANCE)
{
// StartNode to MiddleNode is a straight vertical line
if(gesture->middle.y > gesture->start.y + MINLENGTH)
{
// StartNode to MiddleNode is drawn downwards
if(gesture->middle.y - gesture->end.y < DEVIANCE && gesture->middle.y - gesture->end.y > -DEVIANCE && gesture->end.x > gesture->middle.x + MINLENGTH)
{
// MiddleNode to EndNode is a straight horizontal line (to the right) -> close tab
midori_browser_activate_action(browser, "TabClose");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("TabClose gesture\n");
#endif
}
else if(gesture->middle.y - gesture->end.y < DEVIANCE && gesture->middle.y - gesture->end.y > -DEVIANCE && gesture->end.x < gesture->middle.x - MINLENGTH)
{
// MiddleNode to EndNode is a straight horizontal line (to the left) -> reload
midori_browser_activate_action(browser, "Reload");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("Reload gesture\n");
#endif
}
else if(gesture->end.y == 0 && gesture->end.x == 0)
{
// no EndNode, just a vertical line -> new tab
midori_browser_activate_action(browser, "TabNew");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("TabNew gesture\n");
#endif
}
}
if(gesture->middle.y < gesture->start.y - MINLENGTH)
{
// StartNode to MiddleNode is drawn upwards
if(gesture->end.y == 0 && gesture->end.x == 0)
{
// no EndNode, just a vertical line -> stop
midori_browser_activate_action(browser, "Stop");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("Stop gesture\n");
#endif
}
}
}
else if(gesture->middle.y - gesture->start.y < DEVIANCE && gesture->middle.y - gesture->start.y > -DEVIANCE)
{
// Start Node to MiddleNode is a straight horizontal line
if(gesture->middle.x > gesture->start.x + MINLENGTH)
{
// the line was drawn from left to right
if(gesture->end.x == 0 && gesture->end.y == 0)
{
// we only have one horizontal line from left to right -> forward gesture
midori_browser_activate_action(browser, "Forward");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("Forward gesture\n");
#endif
}
}
else if(gesture->middle.x < gesture->start.x - MINLENGTH)
{
// the line was drawn from right to left
if(gesture->end.x == 0 && gesture->end.y == 0)
{
// we only have one horizontal line from right to left -> backwards gesture
midori_browser_activate_action(browser, "Back");
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("Back gesture\n");
#endif
}
}
}
}
mouse_gesture_clear(gesture); // gesture finished, clear it
return(TRUE);
}
else
return(FALSE); // this event is supposed to be handled again (by someone else's code) since it was irrelevant to us
/* 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 */
midori_browser_activate_action (browser, "Back");
}
}
}
}
mouse_gesture_clear (gesture);
return TRUE;
}
else
return FALSE;
}
static void mouse_gestures_tab_cb(MidoriBrowser* browser, GtkWidget *view) // this is performed when a new tab is created
static void mouse_gestures_tab_cb (MidoriBrowser* browser, GtkWidget *view)
{
g_signal_connect(view, "event", G_CALLBACK(mouse_gestures_handle_events), browser); // perform the above callback when an event from the view is received
return;
g_signal_connect (view, "event", G_CALLBACK (mouse_gestures_handle_events), browser);
}
static void mouse_gestures_browser_cb(MidoriApp *app, MidoriBrowser *browser) // this is performed when ever a new window is created
static void mouse_gestures_browser_cb (MidoriApp *app, MidoriBrowser *browser)
{
g_signal_connect(browser, "add-tab", G_CALLBACK(mouse_gestures_tab_cb), NULL); // connect the above callback to the "add-tab" signal of browser
return;
g_signal_connect (browser, "add-tab", G_CALLBACK (mouse_gestures_tab_cb), NULL);
}
/* this is performed when the extension is deactivated.
disconnect all signal handlers, so that mouse gestures are no longer handled */
static void mouse_gestures_deactivate(MidoriExtension *extension, MidoriApp *app)
static void mouse_gestures_deactivate (MidoriExtension *extension, MidoriApp *app)
{
gulong signal_id = g_signal_handler_find(app, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, mouse_gestures_browser_cb, NULL); // get the signal handler id
if(signal_id != 0) // if that id is valid
g_signal_handler_disconnect(app, signal_id); // disconnect the signal
KatzeArray *browsers = katze_object_get_object(app, "browsers"); // get the array that contains all browsers
guint i; // our counter variable :)
for(i = 0; i < katze_array_get_length(browsers); i++) // from the first to the last browser
{
MidoriBrowser *browser = katze_array_get_nth_item(browsers, i); // get a pointer on the current browser
signal_id = g_signal_handler_find(browser, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, mouse_gestures_tab_cb, NULL); // search forthe signal handler id
if(signal_id != 0) // and if its not invalid..
g_signal_handler_disconnect(browser, signal_id); // disconnect it
gulong signal_id;
KatzeArray *browsers;
guint i;
gint j;
GtkWidget *notebook;
GtkWidget *notebook = katze_object_get_object(browser, "notebook"); // get a pointer on the notebook
gint j; // another counter
for(j = 0; j < gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); j++) // from the first to the last tab
{
GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), j); // get a pointer on the tab's view
signal_id = g_signal_handler_find(page, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, mouse_gestures_handle_events, NULL); // find the signal id of the event handler
if(signal_id != 0) // if the id is valid
g_signal_handler_disconnect(page, signal_id); // disconnect the handler
}
}
g_free(gesture); // free the structure that contains the gesture information
return;
signal_id =
g_signal_handler_find (app, G_SIGNAL_MATCH_FUNC,
0, 0, NULL,
mouse_gestures_browser_cb, NULL);
if(signal_id != 0)
g_signal_handler_disconnect (app, signal_id);
browsers = katze_object_get_object (app, "browsers");
for (i = 0; i < katze_array_get_length (browsers); i++)
{
MidoriBrowser *browser;
browser = katze_array_get_nth_item (browsers, i);
signal_id =
g_signal_handler_find (browser, G_SIGNAL_MATCH_FUNC,
0, 0, NULL,
mouse_gestures_tab_cb, NULL);
if (signal_id != 0)
g_signal_handler_disconnect (browser, signal_id);
notebook = katze_object_get_object (browser, "notebook");
for (j = 0; j < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); j++)
{
GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), j);
signal_id =
g_signal_handler_find (page, G_SIGNAL_MATCH_FUNC,
0, 0, NULL,
mouse_gestures_handle_events, NULL);
if (signal_id != 0)
g_signal_handler_disconnect (page, signal_id);
}
}
g_signal_handlers_disconnect_by_func (extension, mouse_gestures_deactivate, app);
g_free (gesture);
}
static void mouse_gestures_activate(MidoriExtension *extension, MidoriApp *app) // this is performed on extension-activation
static void mouse_gestures_activate (MidoriExtension *extension, MidoriApp *app)
{
g_signal_connect(app, "add-browser", G_CALLBACK(mouse_gestures_browser_cb), NULL); // connect the above callback to the "add-browser" signal of app
g_signal_connect(extension, "deactivate", G_CALLBACK(mouse_gestures_deactivate), app); // connect the deactivate callback to the extension
return;
gesture = mouse_gesture_new ();
g_signal_connect (app, "add-browser",
G_CALLBACK (mouse_gestures_browser_cb), NULL);
g_signal_connect (extension, "deactivate",
G_CALLBACK (mouse_gestures_deactivate), app);
}
MidoriExtension* extension_init(void)
MidoriExtension* extension_init (void)
{
MidoriExtension* extension = NULL;
extension = g_object_new(MIDORI_TYPE_EXTENSION,
"name", _("Mouse Gestures"),
"description", _("Control Midori by moving the mouse"),
"version", MOUSE_GESTURES_VERSION,
"authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL);
g_signal_connect(extension, "activate", G_CALLBACK(mouse_gestures_activate), NULL); // connect the callback that's executed on activation
gesture = NULL; // initialise gesture pointer; initialising pointers is always a good choice
if((gesture = mouse_gesture_new()) == NULL) // allocate space for the gesture
{
// no space could be allocated
g_free(extension); // free the space used by this extension
return(NULL);
}
return(extension);
MidoriExtension* extension;
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Mouse Gestures"),
"description", _("Control Midori by moving the mouse"),
"version", MOUSE_GESTURES_VERSION,
"authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL);
g_signal_connect (extension, "activate",
G_CALLBACK (mouse_gestures_activate), NULL);
return extension;
}

View file

@ -27,7 +27,7 @@ page_holder_notebook_append_view (GtkWidget* notebook)
GtkWidget* label;
view = midori_view_new (NULL);
browser = MIDORI_BROWSER (gtk_widget_get_toplevel (notebook));
browser = midori_browser_get_for_widget (notebook);
settings = katze_object_get_object (browser, "settings");
midori_view_set_settings (MIDORI_VIEW (view), settings);
g_object_unref (settings);
@ -49,7 +49,7 @@ page_holder_button_jump_to_clicked_cb (GtkWidget* button,
if (n < 0)
n = page_holder_notebook_append_view (notebook);
browser = MIDORI_BROWSER (gtk_widget_get_toplevel (notebook));
browser = midori_browser_get_for_widget (notebook);
uri = midori_browser_get_current_uri (browser);
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), n);
midori_view_set_uri (MIDORI_VIEW (view), uri);
@ -66,7 +66,7 @@ page_holder_button_add_clicked_cb (GtkWidget* button,
n = page_holder_notebook_append_view (notebook);
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), n);
browser = MIDORI_BROWSER (gtk_widget_get_toplevel (notebook));
browser = midori_browser_get_for_widget (notebook);
uri = midori_browser_get_current_uri (browser);
midori_view_set_uri (MIDORI_VIEW (view), uri);
}

View file

@ -69,7 +69,7 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
gtk_widget_show (image);
gtk_container_add (GTK_CONTAINER (button), image);
#if GTK_CHECK_VERSION(2, 12, 0)
gtk_widget_set_tooltip_text (button, _("Enable plugins"));
gtk_widget_set_tooltip_text (button, _("Enable Netscape plugins"));
#endif
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
gtk_widget_show (button);
@ -101,8 +101,8 @@ MidoriExtension*
extension_init (void)
{
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", "Statusbar Features",
"description", "",
"name", _("Statusbar Features"),
"description", _("Easily toggle features on web pages on and off"),
"version", "0.1",
"authors", "Christian Dywan <christian@twotoasts.de>",
NULL);

View file

@ -7,6 +7,9 @@ for extension in extensions:
# Tab Panel isn't useful at this point
if extension == 'tab-panel':
continue
# Adblock is incomplete and not ready for release
if extension == 'adblock.c':
continue
folder = 'extensions' + os.sep + extension
if os.path.isdir (folder):
files = os.listdir (folder)

View file

@ -111,16 +111,10 @@ _katze_array_move_item (KatzeArray* array,
static void
_katze_array_clear (KatzeArray* array)
{
guint n;
guint i;
GObject* item;
n = g_list_length (array->items);
for (i = 0; i < n; i++)
{
if ((item = g_list_nth_data (array->items, i)))
katze_array_remove_item (array, item);
}
while ((item = g_list_nth_data (array->items, 0)))
katze_array_remove_item (array, item);
g_list_free (array->items);
array->items = NULL;
}

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Enrico Tröger <enrico.troeger@uvena.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -13,6 +14,7 @@
#include "katze-net.h"
#include "katze-utils.h"
#include "marshal.h"
#include <string.h>
#include <glib/gi18n.h>
@ -44,6 +46,7 @@ enum
{
POPULATE_POPUP,
ACTIVATE_ITEM,
ACTIVATE_ITEM_ALT,
LAST_SIGNAL
};
@ -107,6 +110,30 @@ katze_array_action_class_init (KatzeArrayActionClass* class)
G_TYPE_NONE, 1,
KATZE_TYPE_ITEM);
/**
* KatzeArrayAction::activate-item-alt:
* @array: the object on which the signal is emitted
* @item: the item being activated
* @button: the mouse button pressed
*
* An item was clicked with a particular button. Use this if you need
* to handle middle or right clicks specially.
*
* Return value: %TRUE if the event was handled. If %FALSE is returned,
* the default "activate-item" signal is emitted.
*
* Since: 0.1.7
**/
signals[ACTIVATE_ITEM_ALT] = g_signal_new ("activate-item-alt",
G_TYPE_FROM_CLASS (class),
(GSignalFlags) (G_SIGNAL_RUN_LAST),
0,
0,
NULL,
katze_cclosure_marshal_BOOLEAN__OBJECT_UINT,
G_TYPE_BOOLEAN, 2,
KATZE_TYPE_ITEM, G_TYPE_UINT);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = katze_array_action_finalize;
gobject_class->set_property = katze_array_action_set_property;
@ -206,13 +233,35 @@ katze_array_action_activate (GtkAction* action)
}
static void
katze_array_action_menu_item_activate_cb (GtkWidget* proxy,
KatzeArrayAction* array_action)
katze_array_action_menu_activate_cb (GtkWidget* proxy,
KatzeArrayAction* array_action)
{
KatzeItem* item = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
g_signal_emit (array_action, signals[ACTIVATE_ITEM], 0, item);
}
static gboolean
katze_array_action_menu_button_press_cb (GtkWidget* proxy,
GdkEventButton* event,
KatzeArrayAction* array_action)
{
KatzeItem* item = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
gboolean handled;
g_signal_emit (array_action, signals[ACTIVATE_ITEM_ALT], 0, item,
event->button, &handled);
if (!handled)
g_signal_emit (array_action, signals[ACTIVATE_ITEM], 0, item);
/* we need to block the 'activate' handler which would be called
* otherwise as well */
g_signal_handlers_block_by_func (proxy,
katze_array_action_menu_activate_cb, array_action);
return TRUE;
}
static void
katze_array_action_menu_item_select_cb (GtkWidget* proxy,
KatzeArrayAction* array_action);
@ -268,8 +317,13 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
}
else
{
g_signal_connect (menuitem, "button-press-event",
G_CALLBACK (katze_array_action_menu_button_press_cb), array_action);
/* we need the 'activate' signal as well for keyboard events */
g_signal_connect (menuitem, "activate",
G_CALLBACK (katze_array_action_menu_item_activate_cb), array_action);
G_CALLBACK (katze_array_action_menu_activate_cb), array_action);
}
gtk_widget_show (menuitem);
}
if (!i)
@ -418,6 +472,44 @@ katze_array_action_item_notify_cb (KatzeItem* item,
}
}
static gboolean
katze_array_action_proxy_create_menu_proxy_cb (GtkWidget* proxy,
KatzeItem* item)
{
KatzeArrayAction* array_action;
GtkWidget* menuitem;
const gchar* icon_name;
GtkWidget* image;
GdkPixbuf* icon;
array_action = g_object_get_data (G_OBJECT (proxy), "KatzeArrayAction");
menuitem = katze_image_menu_item_new_ellipsized (
katze_item_get_name (item));
if ((icon_name = katze_item_get_icon (item)) && *icon_name)
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
{
if (KATZE_IS_ARRAY (item))
icon = gtk_widget_render_icon (menuitem,
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
else
icon = katze_net_load_icon (array_action->net,
katze_item_get_uri (item), NULL, proxy, NULL);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
}
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
g_object_set_data (G_OBJECT (menuitem), "KatzeItem", item);
g_signal_connect (menuitem, "button-press-event",
G_CALLBACK (katze_array_action_menu_button_press_cb), array_action);
/* we need the 'activate' signal as well for keyboard events */
g_signal_connect (menuitem, "activate",
G_CALLBACK (katze_array_action_menu_activate_cb), array_action);
gtk_tool_item_set_proxy_menu_item (GTK_TOOL_ITEM (proxy),
"katze-tool-item-menu", menuitem);
return TRUE;
}
/**
* katze_array_action_create_tool_item_for:
* @array_action: a #KatzeArrayAction
@ -453,6 +545,8 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
return gtk_separator_tool_item_new ();
toolitem = gtk_tool_button_new (NULL, NULL);
g_signal_connect (toolitem, "create-menu-proxy",
G_CALLBACK (katze_array_action_proxy_create_menu_proxy_cb), item);
if (KATZE_IS_ARRAY (item))
icon = gtk_widget_render_icon (GTK_WIDGET (toolitem),
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);

View file

@ -219,14 +219,9 @@ katze_property_proxy (gpointer object,
string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget),
string ? string : "");
#if GTK_CHECK_VERSION (2, 12, 0)
g_signal_connect (widget, "file-set",
G_CALLBACK (proxy_file_file_set_cb), object);
#else
if (pspec->flags & G_PARAM_WRITABLE)
g_signal_connect (widget, "selection-changed",
G_CALLBACK (proxy_file_file_set_cb), object);
#endif
}
else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("folder"))
{
@ -238,14 +233,9 @@ katze_property_proxy (gpointer object,
string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
string ? string : "");
#if GTK_CHECK_VERSION (2, 12, 0)
g_signal_connect (widget, "file-set",
G_CALLBACK (proxy_folder_file_set_cb), object);
#else
if (pspec->flags & G_PARAM_WRITABLE)
g_signal_connect (widget, "selection-changed",
G_CALLBACK (proxy_folder_file_set_cb), object);
#endif
}
else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("uri"))
{

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -54,6 +55,22 @@ G_BEGIN_DECLS
lvalue = rvalue; \
}
/**
* katze_strv_assign:
* @lvalue: a string list
* @rvalue: the new value
*
* Frees @lvalue if needed and assigns it the value of @rvalue.
*
* Since: 0.1.7
**/
#define katze_strv_assign(lvalue, rvalue) \
if (1) \
{ \
g_strfreev (lvalue); \
lvalue = rvalue; \
}
GtkWidget*
katze_property_proxy (gpointer object,
const gchar* property,

View file

@ -1 +1,2 @@
VOID:POINTER,INT
BOOLEAN:OBJECT,UINT

View file

@ -28,12 +28,9 @@ gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry,
GdkPixbuf* pixbuf)
{
/* Without this ugly hack pixbuf icons don't work */
if (pixbuf)
{
gtk_widget_hide (GTK_WIDGET (entry));
gtk_entry_set_icon_from_pixbuf (entry, position, pixbuf);
gtk_widget_show (GTK_WIDGET (entry));
}
gtk_widget_hide (GTK_WIDGET (entry));
gtk_entry_set_icon_from_pixbuf (entry, position, pixbuf);
gtk_widget_show (GTK_WIDGET (entry));
}
#else
@ -1266,6 +1263,10 @@ gtk_icon_entry_set_icon_from_stock (GtkIconEntry *entry,
{
GdkPixbuf *pixbuf;
/* FIXME: Due to a bug in GtkIconEntry we need to set a non-NULL icon */
if (! stock_id)
stock_id = GTK_STOCK_INFO;
pixbuf = gtk_widget_render_icon (GTK_WIDGET (entry),
stock_id,
GTK_ICON_SIZE_MENU,

View file

@ -39,6 +39,7 @@ G_BEGIN_DECLS
#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
void
gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry,
GtkEntryIconPosition position,

View file

@ -35,6 +35,7 @@
#else
#define is_writable(_cfg_filename) 1
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
@ -71,7 +72,8 @@ build_config_filename (const gchar* filename)
}
static MidoriWebSettings*
settings_new_from_file (const gchar* filename)
settings_new_from_file (const gchar* filename,
gchar*** extensions)
{
MidoriWebSettings* settings = midori_web_settings_new ();
GKeyFile* key_file = g_key_file_new ();
@ -156,11 +158,17 @@ settings_new_from_file (const gchar* filename)
g_warning (_("Invalid configuration value '%s'"), property);
}
g_free (pspecs);
*extensions = g_key_file_get_keys (key_file, "extensions", NULL, NULL);
g_key_file_free (key_file);
return settings;
}
static gboolean
settings_save_to_file (MidoriWebSettings* settings,
MidoriApp* app,
const gchar* filename,
GError** error)
{
@ -172,6 +180,8 @@ settings_save_to_file (MidoriWebSettings* settings,
GType type;
const gchar* property;
gboolean saved;
KatzeArray* extensions = katze_object_get_object (app, "extensions");
MidoriExtension* extension;
key_file = g_key_file_new ();
class = G_OBJECT_GET_CLASS (settings);
@ -228,6 +238,14 @@ settings_save_to_file (MidoriWebSettings* settings,
g_warning (_("Invalid configuration value '%s'"), property);
}
g_free (pspecs);
i = 0;
while ((extension = katze_array_get_nth_item (extensions, i++)))
if (midori_extension_is_active (extension))
g_key_file_set_boolean (key_file, "extensions",
g_object_get_data (G_OBJECT (extension), "filename"), TRUE);
g_object_unref (extensions);
saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file);
return saved;
@ -710,14 +728,15 @@ midori_app_quit_cb (MidoriApp* app)
static void
settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec)
GParamSpec* pspec,
MidoriApp* app)
{
gchar* config_file;
GError* error;
config_file = build_config_filename ("config");
error = NULL;
if (!settings_save_to_file (settings, config_file, &error))
if (!settings_save_to_file (settings, app, config_file, &error))
{
g_warning (_("The configuration couldn't be saved. %s"), error->message);
g_error_free (error);
@ -1147,13 +1166,13 @@ static gboolean
midori_load_extensions (gpointer data)
{
MidoriApp* app = MIDORI_APP (data);
gchar** active_extensions = g_object_get_data (G_OBJECT (app), "extensions");
KatzeArray* extensions;
const gchar* filename;
MidoriExtension* extension;
guint i;
/* Load extensions */
extensions = katze_array_new (MIDORI_TYPE_EXTENSION);
g_object_set (app, "extensions", extensions, NULL);
if (g_module_supported ())
{
/* FIXME: Read extensions from system data dirs */
@ -1165,6 +1184,8 @@ midori_load_extensions (gpointer data)
extension_dir = g_dir_open (extension_path, 0, NULL);
if (extension_dir != NULL)
{
const gchar* filename;
while ((filename = g_dir_read_name (extension_dir)))
{
gchar* fullname;
@ -1186,6 +1207,8 @@ midori_load_extensions (gpointer data)
extension = extension_init ();
/* FIXME: Validate the extension */
/* Signal that we want the extension to load and save */
g_object_set_data_full (G_OBJECT (extension), "filename",
g_strdup (filename), g_free);
midori_extension_get_config_dir (extension);
}
else
@ -1197,6 +1220,14 @@ midori_load_extensions (gpointer data)
g_warning ("%s", g_module_error ());
}
katze_array_add_item (extensions, extension);
if (active_extensions)
{
guint i = 0;
gchar* name;
while ((name = active_extensions[i++]))
if (!g_strcmp0 (filename, name))
g_signal_emit_by_name (extension, "activate", app);
}
g_object_unref (extension);
}
g_dir_close (extension_dir);
@ -1204,11 +1235,7 @@ midori_load_extensions (gpointer data)
g_free (extension_path);
}
g_object_set (app, "extensions", extensions, NULL);
i = 0;
while ((extension = katze_array_get_nth_item (extensions, i++)))
g_signal_emit_by_name (extension, "activate", app);
g_strfreev (active_extensions);
return FALSE;
}
@ -1318,6 +1345,33 @@ midori_run_script (const gchar* filename)
return 1;
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
static void
snapshot_load_finished_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
gchar* filename)
{
GError* error;
GtkPrintOperation* operation = gtk_print_operation_new ();
GtkPrintOperationAction action = GTK_PRINT_OPERATION_ACTION_EXPORT;
GtkPrintOperationResult result;
gtk_print_operation_set_export_filename (operation, filename);
error = NULL;
result = webkit_web_frame_print_full (web_frame, operation, action, &error);
if (error != NULL)
{
g_error ("%s", error->message);
gtk_main_quit ();
}
g_object_unref (operation);
g_print (_("Snapshot saved to: %s\n"), filename);
gtk_main_quit ();
}
#endif
int
main (int argc,
char** argv)
@ -1325,6 +1379,7 @@ main (int argc,
gchar* webapp;
gchar* config;
gboolean run;
gchar* snapshot;
gboolean version;
gchar** uris;
MidoriApp* app;
@ -1338,6 +1393,10 @@ main (int argc,
N_("Use FOLDER as configuration folder"), N_("FOLDER") },
{ "run", 'r', 0, G_OPTION_ARG_NONE, &run,
N_("Run the specified filename as javascript"), NULL },
#if WEBKIT_CHECK_VERSION (1, 1, 6)
{ "snapshot", 's', 0, G_OPTION_ARG_STRING, &snapshot,
N_("Take a snapshot of the specified URI"), NULL },
#endif
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version,
N_("Display program version"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &uris,
@ -1345,6 +1404,7 @@ main (int argc,
{ NULL }
};
GString* error_messages;
gchar** extensions;
MidoriWebSettings* settings;
gchar* config_file;
MidoriStartup load_on_startup;
@ -1379,6 +1439,7 @@ main (int argc,
webapp = NULL;
config = NULL;
run = FALSE;
snapshot = NULL;
version = FALSE;
uris = NULL;
error = NULL;
@ -1412,6 +1473,39 @@ main (int argc,
return 0;
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
if (snapshot)
{
gchar* filename;
gint fd;
GtkWidget* web_view;
fd = g_file_open_tmp ("snapshot-XXXXXX.pdf", &filename, &error);
close (fd);
error = NULL;
if (error)
{
g_error ("%s", error->message);
return 1;
}
if (g_unlink (filename) == -1)
{
g_error ("%s", g_strerror (errno));
return 1;
}
web_view = webkit_web_view_new ();
g_signal_connect (web_view, "load-finished",
G_CALLBACK (snapshot_load_finished_cb), filename);
webkit_web_view_open (WEBKIT_WEB_VIEW (web_view), snapshot);
gtk_main ();
g_free (filename);
return 0;
}
#endif
/* Web Application support */
if (webapp)
{
@ -1497,7 +1591,7 @@ main (int argc,
error_messages = g_string_new (NULL);
config_file = build_config_filename ("config");
error = NULL;
settings = settings_new_from_file (config_file);
settings = settings_new_from_file (config_file, &extensions);
katze_assign (config_file, build_config_filename ("accels"));
gtk_accel_map_load (config_file);
katze_assign (config_file, build_config_filename ("search"));
@ -1657,7 +1751,7 @@ main (int argc,
katze_assign (config_file, build_config_filename ("config"));
if (is_writable (config_file))
g_signal_connect_after (settings, "notify",
G_CALLBACK (settings_notify_cb), NULL);
G_CALLBACK (settings_notify_cb), app);
katze_assign (config_file, build_config_filename ("search"));
if (is_writable (config_file))
@ -1746,6 +1840,7 @@ main (int argc,
G_CALLBACK (midori_app_add_browser_cb), NULL);
g_idle_add (midori_load_cookie_jar, settings);
g_object_set_data (G_OBJECT (app), "extensions", extensions);
g_idle_add (midori_load_extensions, app);
katze_item_set_parent (KATZE_ITEM (_session), app);
g_idle_add (midori_load_session, _session);

View file

@ -4,3 +4,4 @@ VOID:BOOLEAN,STRING
VOID:OBJECT,ENUM
VOID:STRING,BOOLEAN
VOID:STRING,INT,STRING
VOID:STRING,STRING

View file

@ -24,6 +24,20 @@
#include <unique/unique.h>
#endif
typedef struct _NotifyNotification NotifyNotification;
typedef struct
{
gboolean (*init) (const gchar* app_name);
void (*uninit) (void);
NotifyNotification* (*notification_new) (const gchar* summary,
const gchar* body,
const gchar* icon,
GtkWidget* attach);
gboolean (*notification_show) (NotifyNotification* notification,
GError** error);
} LibNotifyFuncs;
struct _MidoriApp
{
GObject parent_instance;
@ -41,6 +55,11 @@ struct _MidoriApp
KatzeArray* browsers;
gpointer instance;
/* libnotify handling */
gchar* program_notify_send;
GModule* libnotify_module;
LibNotifyFuncs libnotify_funcs;
};
struct _MidoriAppClass
@ -52,6 +71,9 @@ struct _MidoriAppClass
(*add_browser) (MidoriApp* app,
MidoriBrowser* browser);
void
(*remove_browser) (MidoriApp* app,
MidoriBrowser* browser);
void
(*quit) (MidoriApp* app);
};
@ -75,6 +97,7 @@ enum
enum {
ADD_BROWSER,
REMOVE_BROWSER,
QUIT,
LAST_SIGNAL
@ -85,6 +108,9 @@ static guint signals[LAST_SIGNAL];
static void
midori_app_finalize (GObject* object);
static void
midori_app_init_libnotify (MidoriApp* app);
static void
midori_app_set_property (GObject* object,
guint prop_id,
@ -109,13 +135,18 @@ midori_browser_focus_in_event_cb (MidoriBrowser* browser,
static void
midori_browser_new_window_cb (MidoriBrowser* browser,
const gchar* uri,
MidoriBrowser* new_browser,
MidoriApp* app)
{
MidoriBrowser* new_browser = midori_app_create_browser (app);
midori_app_add_browser (app, new_browser);
g_object_set (new_browser,
"settings", app->settings,
"bookmarks", app->bookmarks,
"trash", app->trash,
"search-engines", app->search_engines,
"history", app->history,
NULL);
midori_browser_add_uri (new_browser, uri);
midori_app_add_browser (app, new_browser);
gtk_widget_show (GTK_WIDGET (new_browser));
}
@ -131,6 +162,7 @@ static gboolean
midori_browser_destroy_cb (MidoriBrowser* browser,
MidoriApp* app)
{
g_signal_emit (app, signals[REMOVE_BROWSER], 0, browser);
katze_array_remove_item (app->browsers, browser);
if (!katze_array_is_empty (app->browsers))
return FALSE;
@ -160,6 +192,8 @@ _midori_app_add_browser (MidoriApp* app,
"signal::destroy", midori_browser_destroy_cb, app,
"signal::quit", midori_browser_quit_cb, app,
NULL);
g_signal_connect_swapped (browser, "send-notification",
G_CALLBACK (midori_app_send_notification), app);
katze_array_add_item (app->browsers, browser);
@ -191,6 +225,26 @@ midori_app_class_init (MidoriAppClass* class)
G_TYPE_NONE, 1,
MIDORI_TYPE_BROWSER);
/**
* MidoriApp::remove-browser:
* @app: the object on which the signal is emitted
* @browser: a #MidoriBrowser
*
* A new browser is being added to the app.
*
* Since: 0.1.7
*/
signals[REMOVE_BROWSER] = g_signal_new (
"remove-browser",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
MIDORI_TYPE_BROWSER);
signals[QUIT] = g_signal_new (
"quit",
G_TYPE_FROM_CLASS (class),
@ -461,6 +515,8 @@ midori_app_init (MidoriApp* app)
app->browsers = katze_array_new (MIDORI_TYPE_BROWSER);
app->instance = NULL;
midori_app_init_libnotify (app);
}
static void
@ -481,6 +537,13 @@ midori_app_finalize (GObject* object)
katze_object_assign (app->instance, NULL);
if (app->libnotify_module)
{
app->libnotify_funcs.uninit ();
g_module_close (app->libnotify_module);
}
katze_assign (app->program_notify_send, NULL);
G_OBJECT_CLASS (midori_app_parent_class)->finalize (object);
}
@ -752,7 +815,7 @@ midori_app_add_browser (MidoriApp* app,
*
* Return value: a new #MidoriBrowser
*
* Since: 1.0.2
* Since: 0.1.2
**/
MidoriBrowser*
midori_app_create_browser (MidoriApp* app)
@ -783,3 +846,86 @@ midori_app_quit (MidoriApp* app)
g_signal_emit (app, signals[QUIT], 0);
}
static void
midori_app_init_libnotify (MidoriApp* app)
{
gint i;
const gchar* sonames[] = { "libnotify.so", "libnotify.so.1", NULL };
for (i = 0; sonames[i] != NULL && app->libnotify_module == NULL; i++ )
{
app->libnotify_module = g_module_open (sonames[i], G_MODULE_BIND_LOCAL);
}
if (app->libnotify_module != NULL)
{
g_module_symbol (app->libnotify_module, "notify_init",
(void*) &(app->libnotify_funcs.init));
g_module_symbol (app->libnotify_module, "notify_uninit",
(void*) &(app->libnotify_funcs.uninit));
g_module_symbol (app->libnotify_module, "notify_notification_new",
(void*) &(app->libnotify_funcs.notification_new));
g_module_symbol (app->libnotify_module, "notify_notification_show",
(void*) &(app->libnotify_funcs.notification_show));
/* init libnotify */
if (!app->libnotify_funcs.init || !app->libnotify_funcs.init ("midori"))
{
g_module_close (app->libnotify_module);
app->libnotify_module = NULL;
}
}
app->program_notify_send = g_find_program_in_path ("notify-send");
}
/**
* midori_app_send_notification:
* @app: a #MidoriApp
* @title: title of the notification
* @message: text of the notification, or NULL
*
* Send #message to the notification daemon to display it.
* This is done by using libnotify if available or by using the program
* "notify-send" as a fallback.
*
* There is no guarantee that the message have been sent and displayed, as
* neither libnotify nor "notify-send" might be available or the
* notification daemon might not be running.
*
* Since 0.1.7
**/
void
midori_app_send_notification (MidoriApp* app,
const gchar* title,
const gchar* message)
{
gboolean sent = FALSE;
g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (title);
if (app->libnotify_module)
{
NotifyNotification* n;
n = app->libnotify_funcs.notification_new (title, message, "midori", NULL);
sent = app->libnotify_funcs.notification_show (n, NULL);
g_object_unref (n);
}
/* Fall back to the command line program "notify-send" */
if (!sent && app->program_notify_send)
{
gchar* msgq = g_shell_quote (message);
gchar* titleq = g_shell_quote (title);
gchar* command = g_strdup_printf ("%s -i midori %s %s",
app->program_notify_send, titleq, msgq);
g_spawn_command_line_async (command, NULL);
g_free (titleq);
g_free (msgq);
g_free (command);
}
}

View file

@ -64,6 +64,11 @@ midori_app_create_browser (MidoriApp* app);
void
midori_app_quit (MidoriApp* app);
void
midori_app_send_notification (MidoriApp* app,
const gchar* title,
const gchar* message);
G_END_DECLS
#endif /* __MIDORI_APP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -88,6 +88,11 @@ void
midori_browser_remove_tab (MidoriBrowser* browser,
GtkWidget* widget);
void
midori_browser_foreach (MidoriBrowser* browser,
GtkCallback callback,
gpointer callback_data);
gint
midori_browser_add_item (MidoriBrowser* browser,
KatzeItem* item);
@ -127,6 +132,9 @@ midori_browser_get_current_tab (MidoriBrowser* browser);
KatzeArray*
midori_browser_get_proxy_array (MidoriBrowser* browser);
MidoriBrowser*
midori_browser_get_for_widget (GtkWidget* widget);
void
midori_browser_quit (MidoriBrowser* browser);

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2008-2009 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -29,7 +30,7 @@ struct _MidoriExtensionPrivate
gchar* authors;
MidoriApp* app;
gboolean active;
gint active;
gchar* config_dir;
GList* lsettings;
GHashTable* settings;
@ -60,20 +61,37 @@ typedef struct
gchar* value;
} MESettingString;
typedef struct
{
gchar* name;
GType type;
gchar** default_value;
gchar** value;
gsize default_length;
gsize length;
} MESettingStringList;
void me_setting_free (gpointer setting)
{
MESettingString* string_setting = (MESettingString*)setting;
MESettingStringList* strlist_setting = (MESettingStringList*)setting;
g_free (string_setting->name);
if (string_setting->type == G_TYPE_STRING)
{
g_free (string_setting->name);
g_free (string_setting->default_value);
g_free (string_setting->value);
}
if (strlist_setting->type == G_TYPE_STRV)
{
g_free (strlist_setting->name);
g_strfreev (strlist_setting->default_value);
g_strfreev (strlist_setting->value);
}
}
#define midori_extension_can_install_setting(extension, name) \
if (extension->priv->active) \
if (extension->priv->active > 0) \
{ \
g_critical ("%s: Settings have to be installed before " \
"the extension is activated.", G_STRFUNC); \
@ -276,6 +294,19 @@ midori_extension_activate_cb (MidoriExtension* extension,
else
setting->value = g_strdup (setting->default_value);
}
else if (setting->type == G_TYPE_STRV)
{
MESettingStringList* setting_ = (MESettingStringList*)setting;
if (extension->priv->key_file)
{
setting_->value = sokoke_key_file_get_string_list_default (
extension->priv->key_file,
"settings", setting->name, &setting_->length,
setting_->default_value, &setting_->default_length, NULL);
}
else
setting_->value = g_strdupv (setting_->default_value);
}
else
g_assert_not_reached ();
@ -283,7 +314,7 @@ midori_extension_activate_cb (MidoriExtension* extension,
}
extension->priv->app = g_object_ref (app);
extension->priv->active = TRUE;
extension->priv->active = 1;
/* FIXME: Disconnect all signal handlers */
}
@ -294,7 +325,7 @@ midori_extension_init (MidoriExtension* extension)
MIDORI_TYPE_EXTENSION, MidoriExtensionPrivate);
extension->priv->app = NULL;
extension->priv->active = FALSE;
extension->priv->active = 0;
extension->priv->config_dir = NULL;
extension->priv->lsettings = NULL;
extension->priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal,
@ -416,7 +447,30 @@ midori_extension_is_active (MidoriExtension* extension)
{
g_return_val_if_fail (MIDORI_IS_EXTENSION (extension), FALSE);
return extension->priv->active;
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;
}
/**
@ -430,8 +484,9 @@ midori_extension_deactivate (MidoriExtension* extension)
{
g_return_if_fail (midori_extension_is_active (extension));
extension->priv->active = 2;
g_signal_emit (extension, signals[DEACTIVATE], 0);
extension->priv->active = FALSE;
extension->priv->active = 0;
katze_object_assign (extension->priv->app, NULL);
}
@ -459,8 +514,7 @@ midori_extension_get_app (MidoriExtension* extension)
* @extension: a #MidoriExtension
*
* Retrieves the path to a directory reserved for configuration
* files specific to the extension. For that purpose the 'name'
* of the extension is actually part of the path.
* files specific to the extension.
*
* If settings are installed on the extension, they will be
* loaded from and saved to a file "config" in this path.
@ -470,12 +524,16 @@ midori_extension_get_app (MidoriExtension* extension)
const gchar*
midori_extension_get_config_dir (MidoriExtension* extension)
{
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");
g_return_val_if_fail (filename != NULL, NULL);
extension->priv->config_dir = g_build_filename (
sokoke_set_config_dir (NULL), "extensions",
extension->priv->name, NULL);
sokoke_set_config_dir (NULL), "extensions", filename, NULL);
}
return extension->priv->config_dir;
}
@ -770,3 +828,115 @@ midori_extension_set_string (MidoriExtension* extension,
}
}
}
/**
* midori_extension_install_string_list:
* @extension: a #MidoriExtension
* @name: the name of the setting
* @default_value: the default value
*
* Installs a string list that can be used to conveniently
* store user configuration.
*
* Note that all settings have to be installed before
* the extension is activated.
*
* Since: 0.1.7
**/
void
midori_extension_install_string_list (MidoriExtension* extension,
const gchar* name,
gchar** default_value,
gsize default_length)
{
MESettingStringList* setting;
g_return_if_fail (midori_extension_is_prepared (extension));
midori_extension_can_install_setting (extension, name);
me_setting_install (MESettingStringList, g_strdup (name), G_TYPE_STRV,
g_strdupv (default_value), NULL);
setting->default_length = default_length;
}
/**
* midori_extension_get_string_list:
* @extension: a #MidoriExtension
* @name: the name of the setting
* @length: return location to store number of strings, or %NULL
*
* Retrieves the value of the specified setting.
*
* Return value: a newly allocated NULL-terminated list of strings,
* should be freed with g_strfreev()
*
* Since: 0.1.7
**/
gchar**
midori_extension_get_string_list (MidoriExtension* extension,
const gchar* name,
gsize* length)
{
MESettingStringList* setting;
g_return_val_if_fail (midori_extension_is_prepared (extension), NULL);
g_return_val_if_fail (name != NULL, NULL);
setting = g_hash_table_lookup (extension->priv->settings, name);
me_setting_type (setting, G_TYPE_STRV, return NULL);
if (length)
*length = setting->length;
return g_strdupv (setting->value);
}
/**
* midori_extension_set_string_list:
* @extension: a #MidoriExtension
* @name: the name of the setting
* @value: the new value
* @length: number of strings in @value, or G_MAXSIZE
*
* Assigns a new value to the specified setting.
*
* Since: 0.1.7
**/
void
midori_extension_set_string_list (MidoriExtension* extension,
const gchar* name,
gchar** value,
gsize length)
{
MESettingStringList* setting;
g_return_if_fail (midori_extension_is_active (extension));
g_return_if_fail (name != NULL);
setting = g_hash_table_lookup (extension->priv->settings, name);
me_setting_type (setting, G_TYPE_STRV, return);
katze_strv_assign (setting->value, g_strdupv (value));
setting->length = length;
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);
g_mkdir_with_parents (extension->priv->config_dir, 0700);
g_key_file_set_string_list (extension->priv->key_file,
"settings", name, (const gchar**)value, length);
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);
}
}
}

View file

@ -54,6 +54,9 @@ midori_extension_is_prepared (MidoriExtension* extension);
gboolean
midori_extension_is_active (MidoriExtension* extension);
gboolean
midori_extension_is_deactivating (MidoriExtension* extension);
void
midori_extension_deactivate (MidoriExtension* extension);
@ -105,6 +108,23 @@ midori_extension_set_string (MidoriExtension* extension,
const gchar* name,
const gchar* value);
void
midori_extension_install_string_list (MidoriExtension* extension,
const gchar* name,
gchar** default_value,
gsize default_length);
gchar**
midori_extension_get_string_list (MidoriExtension* extension,
const gchar* name,
gsize* length);
void
midori_extension_set_string_list (MidoriExtension* extension,
const gchar* name,
gchar** value,
gsize length);
G_END_DECLS
#endif /* __MIDORI_EXTENSION_H__ */

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008-2009 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
@ -189,12 +189,23 @@ midori_location_action_class_init (MidoriLocationActionClass* class)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
/* Allow this to be used in tests, it's otherwise private */
/*static*/ GtkWidget*
midori_location_action_entry_for_proxy (GtkWidget* proxy)
{
GtkWidget* alignment = gtk_bin_get_child (GTK_BIN (proxy));
GtkWidget* hbox = gtk_bin_get_child (GTK_BIN (alignment));
GList* children = gtk_container_get_children (GTK_CONTAINER (hbox));
GtkWidget* entry = g_list_nth_data (children, 0);
g_list_free (children);
return entry;
}
static void
midori_location_action_set_model (MidoriLocationAction* location_action,
GtkTreeModel* model)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
@ -203,8 +214,7 @@ midori_location_action_set_model (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
g_object_set (location_entry, "model", model, NULL);
@ -379,7 +389,6 @@ static void
midori_location_action_activate (GtkAction* action)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry;
proxies = gtk_action_get_proxies (action);
@ -387,8 +396,7 @@ midori_location_action_activate (GtkAction* action)
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = midori_location_action_entry_for_proxy (proxies->data);
/* Obviously only one widget can end up with the focus.
Yet we can't predict which one that is, can we? */
@ -399,22 +407,51 @@ midori_location_action_activate (GtkAction* action)
GTK_ACTION_CLASS (midori_location_action_parent_class)->activate (action);
}
static void
midori_location_action_go_clicked_cb (GtkWidget* button,
GtkAction* action)
{
GtkWidget* hbox = gtk_widget_get_parent (button);
GList* children = gtk_container_get_children (GTK_CONTAINER (hbox));
GtkWidget* location_entry = g_list_nth_data (children, 0);
g_list_free (children);
GtkWidget* entry = gtk_bin_get_child (GTK_BIN (location_entry));
const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry));
if (uri && *uri)
g_signal_emit (action, signals[SUBMIT_URI], 0, uri, FALSE);
}
static GtkWidget*
midori_location_action_create_tool_item (GtkAction* action)
{
GtkWidget* toolitem;
GtkWidget* location_entry;
GtkWidget* alignment;
GtkWidget* hbox;
GtkWidget* location_entry;
GtkWidget* go_button;
GtkWidget* go_icon;
toolitem = GTK_WIDGET (gtk_tool_item_new ());
gtk_tool_item_set_expand (GTK_TOOL_ITEM (toolitem), TRUE);
location_entry = midori_location_entry_new ();
alignment = gtk_alignment_new (0.0f, 0.5f, 1.0f, 0.1f);
gtk_container_add (GTK_CONTAINER (alignment), location_entry);
gtk_widget_show (location_entry);
gtk_container_add (GTK_CONTAINER (toolitem), alignment);
gtk_widget_show (alignment);
gtk_container_add (GTK_CONTAINER (toolitem), alignment);
hbox = gtk_hbox_new (FALSE, 0);
gtk_widget_show (hbox);
gtk_container_add (GTK_CONTAINER (alignment), hbox);
location_entry = midori_location_entry_new ();
gtk_widget_show (location_entry);
gtk_box_pack_start (GTK_BOX (hbox), location_entry, TRUE, TRUE, 0);
go_button = gtk_button_new ();
gtk_button_set_focus_on_click (GTK_BUTTON (go_button), FALSE);
gtk_button_set_relief (GTK_BUTTON (go_button), GTK_RELIEF_NONE);
go_icon = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
gtk_button_set_image (GTK_BUTTON (go_button), go_icon);
gtk_widget_show (go_button);
gtk_box_pack_start (GTK_BOX (hbox), go_button, FALSE, FALSE, 0);
g_signal_connect (go_button, "clicked",
G_CALLBACK (midori_location_action_go_clicked_cb), action);
return toolitem;
}
@ -620,6 +657,10 @@ midori_location_action_set_item (MidoriLocationAction* location_action,
GdkPixbuf* icon;
GdkPixbuf* new_icon;
/* Ensure we keep the title if we added the same URI with a title before */
if (!item->title)
gtk_tree_model_get (location_action->model, iter, TITLE_COL, &item->title, -1);
gtk_list_store_set (GTK_LIST_STORE (location_action->model), iter,
URI_COL, item->uri, TITLE_COL, item->title, -1);
@ -750,10 +791,7 @@ midori_location_entry_action_activated_cb (GtkEntryCompletion* completion,
gchar* search;
if (!item)
return;
if (strstr (uri, "%s"))
search = g_strdup_printf (uri, keywords);
else
search = g_strconcat (uri, " ", keywords, NULL);
search = sokoke_search_uri (uri, keywords);
midori_location_action_set_uri (location_action, search);
g_signal_emit (location_action, signals[SUBMIT_URI], 0, search, FALSE);
g_free (search);
@ -858,7 +896,6 @@ static void
midori_location_action_connect_proxy (GtkAction* action,
GtkWidget* proxy)
{
GtkWidget* alignment;
GtkWidget* entry;
MidoriLocationAction* location_action;
GtkCellRenderer* renderer;
@ -873,8 +910,7 @@ midori_location_action_connect_proxy (GtkAction* action,
if (GTK_IS_TOOL_ITEM (proxy))
{
alignment = gtk_bin_get_child (GTK_BIN (proxy));
entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = midori_location_action_entry_for_proxy (proxy);
midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry),
MIDORI_LOCATION_ACTION (action)->progress);
@ -942,7 +978,6 @@ midori_location_action_set_text (MidoriLocationAction* location_action,
const gchar* text)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
GtkTreeIter iter;
@ -960,8 +995,7 @@ midori_location_action_set_text (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_entry_set_text (GTK_ENTRY (entry), text);
@ -990,7 +1024,6 @@ midori_location_action_set_icon (MidoriLocationAction* location_action,
GdkPixbuf* icon)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
@ -1002,8 +1035,7 @@ midori_location_action_set_icon (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (entry),
@ -1121,7 +1153,6 @@ midori_location_action_add_item (MidoriLocationAction* location_action,
const gchar* title)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
MidoriLocationEntryItem item;
@ -1147,8 +1178,7 @@ midori_location_action_add_item (MidoriLocationAction* location_action,
if (GTK_IS_TOOL_ITEM (proxies->data) &&
!strcmp (location_action->uri, uri))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (entry),
@ -1162,7 +1192,6 @@ midori_location_action_set_icon_for_uri (MidoriLocationAction* location_action,
const gchar* uri)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
MidoriLocationEntryItem item;
@ -1182,8 +1211,7 @@ midori_location_action_set_icon_for_uri (MidoriLocationAction* location_action,
if (GTK_IS_TOOL_ITEM (proxies->data) &&
!g_strcmp0 (location_action->uri, uri))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (entry),
@ -1223,7 +1251,6 @@ midori_location_action_set_search_engines (MidoriLocationAction* location_action
KatzeArray* search_engines)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry;
GtkWidget* child;
GtkEntryCompletion* completion;
@ -1241,8 +1268,7 @@ midori_location_action_set_search_engines (MidoriLocationAction* location_action
KatzeItem* item;
guint i;
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = midori_location_action_entry_for_proxy (proxies->data);
child = gtk_bin_get_child (GTK_BIN (entry));
completion = gtk_entry_get_completion (GTK_ENTRY (child));
@ -1270,7 +1296,6 @@ midori_location_action_set_progress (MidoriLocationAction* location_action,
gdouble progress)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry;
g_return_if_fail (MIDORI_IS_LOCATION_ACTION (location_action));
@ -1282,8 +1307,7 @@ midori_location_action_set_progress (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = midori_location_action_entry_for_proxy (proxies->data);
midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry),
location_action->progress);
@ -1295,7 +1319,6 @@ midori_location_action_set_secondary_icon (MidoriLocationAction* location_action
const gchar* stock_id)
{
GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry;
GtkWidget* child;
GtkStockItem stock_item;
@ -1310,16 +1333,11 @@ midori_location_action_set_secondary_icon (MidoriLocationAction* location_action
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
alignment = gtk_bin_get_child (GTK_BIN (proxies->data));
entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = midori_location_action_entry_for_proxy (proxies->data);
child = gtk_bin_get_child (GTK_BIN (entry));
if (stock_id)
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (child),
GTK_ICON_ENTRY_SECONDARY, stock_id);
else
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (child),
GTK_ICON_ENTRY_SECONDARY, NULL);
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (child),
GTK_ICON_ENTRY_SECONDARY, stock_id);
}
}

View file

@ -25,6 +25,7 @@ struct _MidoriPanel
GtkWidget* toolbar;
GtkToolItem* button_align;
GtkToolItem* button_detach;
GtkWidget* toolbar_label;
GtkWidget* frame;
GtkWidget* toolbook;
@ -195,13 +196,15 @@ midori_panel_detached_window_delete_event_cb (GtkWidget* window,
GtkWidget* toolbar = g_object_get_data (G_OBJECT (scrolled), "panel-toolbar");
GtkWidget* menuitem = g_object_get_data (G_OBJECT (scrolled), "panel-menuitem");
GtkToolItem* toolitem;
gint n;
g_object_ref (toolbar);
gtk_container_remove (GTK_CONTAINER (vbox), toolbar);
gtk_container_add (GTK_CONTAINER (panel->toolbook), toolbar);
g_object_unref (toolbar);
g_object_ref (scrolled);
gtk_container_remove (GTK_CONTAINER (vbox), scrolled);
gtk_container_add (GTK_CONTAINER (panel->notebook), scrolled);
n = gtk_notebook_append_page (GTK_NOTEBOOK (panel->notebook), scrolled, NULL);
g_object_unref (scrolled);
toolitem = midori_panel_construct_tool_item (panel,
MIDORI_VIEWABLE (_midori_panel_child_for_scrolled (panel, scrolled)));
@ -210,9 +213,20 @@ midori_panel_detached_window_delete_event_cb (GtkWidget* window,
gtk_widget_show (menuitem);
g_object_set_data (G_OBJECT (menuitem), "toolitem", toolitem);
}
midori_panel_set_current_page (panel, n);
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (toolitem), TRUE);
return FALSE;
}
static void
midori_panel_widget_destroy_cb (GtkWidget* viewable,
GtkWidget* widget)
{
gtk_widget_destroy (widget);
g_signal_handlers_disconnect_by_func (
viewable, midori_panel_widget_destroy_cb, widget);
}
static void
midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
MidoriPanel* panel)
@ -221,7 +235,6 @@ midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
/* FIXME: What happens when the browser is destroyed? */
/* FIXME: What about multiple browsers? */
/* FIXME: Should we remember if the child was detached? */
/* FIXME: Fix label of the sidepanel after removing the widgets */
gint n = midori_panel_get_current_page (panel);
GtkToolItem* toolitem = gtk_toolbar_get_nth_item (
GTK_TOOLBAR (panel->toolbar), n);
@ -242,6 +255,9 @@ midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
gtk_container_add (GTK_CONTAINER (window), vbox);
if (menuitem)
gtk_widget_hide (menuitem);
g_signal_handlers_disconnect_by_func (
_midori_panel_child_for_scrolled (panel, scrolled),
midori_panel_widget_destroy_cb, toolitem);
gtk_container_remove (GTK_CONTAINER (panel->toolbar), GTK_WIDGET (toolitem));
g_object_ref (toolbar);
gtk_container_remove (GTK_CONTAINER (panel->toolbook), toolbar);
@ -252,6 +268,12 @@ midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
gtk_container_remove (GTK_CONTAINER (panel->notebook), scrolled);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
g_object_unref (scrolled);
midori_panel_set_current_page (panel, n > 0 ? n - 1 : 0);
toolitem = gtk_toolbar_get_nth_item (GTK_TOOLBAR (panel->toolbar),
n > 0 ? n - 1 : 0);
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (toolitem), TRUE);
if (!gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->notebook)))
gtk_widget_set_sensitive (toolbutton, FALSE);
g_signal_connect (window, "delete-event",
G_CALLBACK (midori_panel_detached_window_delete_event_cb), panel);
gtk_widget_show (window);
@ -305,6 +327,8 @@ midori_panel_init (MidoriPanel* panel)
gtk_container_set_border_width (GTK_CONTAINER (toolitem), 6);
gtk_toolbar_insert (GTK_TOOLBAR (labelbar), toolitem, -1);
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_FULLSCREEN);
gtk_widget_set_sensitive (GTK_WIDGET (toolitem), FALSE);
panel->button_detach = toolitem;
gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem),
_("Detach chosen panel from the window"));
gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (toolitem),
@ -518,22 +542,14 @@ static void
midori_panel_viewable_destroy_cb (GtkWidget* viewable,
MidoriPanel* panel)
{
gint i = gtk_notebook_page_num (GTK_NOTEBOOK (panel->notebook), viewable);
gint i = gtk_notebook_page_num (GTK_NOTEBOOK (panel->notebook),
g_object_get_data (G_OBJECT (viewable), "parent"));
if (i > -1)
gtk_notebook_remove_page (GTK_NOTEBOOK (panel->notebook), i);
gtk_notebook_remove_page (GTK_NOTEBOOK (panel->notebook), i);
g_signal_handlers_disconnect_by_func (
viewable, midori_panel_viewable_destroy_cb, panel);
}
static void
midori_panel_widget_destroy_cb (GtkWidget* viewable,
GtkWidget* widget)
{
gtk_widget_destroy (widget);
g_signal_handlers_disconnect_by_func (
viewable, midori_panel_widget_destroy_cb, widget);
}
static GtkToolItem*
midori_panel_construct_tool_item (MidoriPanel* panel,
MidoriViewable* viewable)
@ -560,6 +576,10 @@ midori_panel_construct_tool_item (MidoriPanel* panel,
gtk_toolbar_insert (GTK_TOOLBAR (panel->toolbar), toolitem, -1);
g_signal_connect (viewable, "destroy",
G_CALLBACK (midori_panel_widget_destroy_cb), toolitem);
if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->notebook)))
gtk_widget_set_sensitive (GTK_WIDGET (panel->button_detach), TRUE);
return toolitem;
}
@ -648,6 +668,7 @@ midori_panel_append_page (MidoriPanel* panel,
G_CALLBACK (midori_panel_widget_destroy_cb), menuitem);
}
g_object_set_data (G_OBJECT (viewable), "parent", scrolled);
g_signal_connect (viewable, "destroy",
G_CALLBACK (midori_panel_viewable_destroy_cb), panel);

View file

@ -419,89 +419,60 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
}
FILLED_ADD (hbox, 1, 2, 1, 2);
button = katze_property_proxy (settings, "show-crash-dialog", NULL);
SPANNED_ADD (button, 0, 2, 2, 3);
INDENTED_ADD (button, 0, 1, 2, 3);
button = katze_property_proxy (settings, "speed-dial-in-new-tabs", NULL);
FILLED_ADD (button, 1, 2, 2, 3);
FRAME_NEW (_("Transfers"));
TABLE_NEW (4, 2);
#if WEBKIT_CHECK_VERSION (1, 1, 3)
TABLE_NEW (2, 2);
label = katze_property_label (settings, "download-folder");
INDENTED_ADD (label, 0, 1, 0, 1);
button = katze_property_proxy (settings, "download-folder", "folder");
FILLED_ADD (button, 1, 2, 0, 1);
#endif
label = katze_property_label (settings, "download-manager");
label = katze_property_proxy (settings, "ask-for-destination-folder", NULL);
gtk_widget_set_sensitive (label, FALSE);
INDENTED_ADD (label, 0, 1, 1, 2);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "download-manager", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 1, 2);
label = katze_property_label (settings, "text-editor");
INDENTED_ADD (label, 0, 1, 2, 3);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "text-editor", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 2, 3);
label = katze_property_label (settings, "news-aggregator");
INDENTED_ADD (label, 0, 1, 3, 4);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "news-aggregator", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 3, 4);
button = katze_property_proxy (settings, "notify-transfer-completed", NULL);
/* FIXME: Disable the option if notifications presumably cannot be sent
gtk_widget_set_sensitive (button, FALSE); */
SPANNED_ADD (button, 1, 2, 1, 2);
/* Page "Appearance" */
PAGE_NEW (GTK_STOCK_SELECT_FONT, _("Appearance"));
FRAME_NEW (_("Font settings"));
TABLE_NEW (6, 2);
TABLE_NEW (7, 2);
label = gtk_label_new (_("Default Font Family"));
INDENTED_ADD (label, 0, 1, 0, 1);
hbox = gtk_hbox_new (FALSE, 4);
button = katze_property_proxy (settings, "default-font-family", "font");
gtk_widget_set_tooltip_text (button, _("The default font family used to display text"));
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
entry = katze_property_proxy (settings, "default-font-size", NULL);
gtk_widget_set_tooltip_text (entry, _("The default font size used to display text"));
gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 4);
FILLED_ADD (hbox, 1, 2, 0, 1);
label = gtk_label_new (_("Minimum Font Size"));
label = gtk_label_new (_("Fixed-width Font Family"));
INDENTED_ADD (label, 0, 1, 1, 2);
button = katze_property_proxy (settings, "monospace-font-family", "font");
gtk_widget_set_tooltip_text (button, _("The font family used to display fixed-width text"));
INDENTED_ADD (button, 1, 2, 1, 2);
label = gtk_label_new (_("Minimum Font Size"));
INDENTED_ADD (label, 0, 1, 2, 3);
entry = katze_property_proxy (settings, "minimum-font-size", NULL);
gtk_widget_set_tooltip_text (entry, _("The minimum font size used to display text"));
INDENTED_ADD (entry, 1, 2, 1, 2);
INDENTED_ADD (entry, 1, 2, 2, 3);
label = katze_property_label (settings, "preferred-encoding");
INDENTED_ADD (label, 0, 1, 2, 3);
INDENTED_ADD (label, 0, 1, 3, 4);
button = katze_property_proxy (settings, "preferred-encoding", NULL);
FILLED_ADD (button, 1, 2, 2, 3);
FILLED_ADD (button, 1, 2, 3, 4);
label = katze_property_label (settings, "default-encoding");
gtk_label_set_label (GTK_LABEL (label), _("Encoding"));
INDENTED_ADD (label, 0, 1, 3, 4);
INDENTED_ADD (label, 0, 1, 4, 5);
entry = katze_property_proxy (settings, "default-encoding", NULL);
gtk_widget_set_tooltip_text (entry, _("The character encoding to use by default"));
g_signal_connect (settings, "notify::preferred-encoding",
G_CALLBACK (midori_preferences_notify_preferred_encoding_cb), entry);
midori_preferences_notify_preferred_encoding_cb (settings, NULL, entry);
FILLED_ADD (entry, 1, 2, 3, 4);
FILLED_ADD (entry, 1, 2, 4, 5);
/* Page "Behavior" */
PAGE_NEW (GTK_STOCK_SELECT_COLOR, _("Behavior"));
@ -528,8 +499,8 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
gtk_widget_set_tooltip_text (button, _("Enable embedded scripting languages"));
INDENTED_ADD (button, 0, 1, 2, 3);
button = katze_property_proxy (settings, "enable-plugins", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Enable plugins"));
gtk_widget_set_tooltip_text (button, _("Enable embedded plugin objects"));
gtk_button_set_label (GTK_BUTTON (button), _("Enable Netscape plugins"));
gtk_widget_set_tooltip_text (button, _("Enable embedded Netscape plugin objects"));
SPANNED_ADD (button, 1, 2, 2, 3);
button = katze_property_proxy (settings, "enforce-96-dpi", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Enforce 96 dots per inch"));
@ -543,6 +514,18 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
SPANNED_ADD (button, 0, 1, 4, 5);
button = katze_property_proxy (settings, "find-while-typing", NULL);
SPANNED_ADD (button, 1, 2, 4, 5);
#if WEBKIT_CHECK_VERSION (1, 1, 6)
FRAME_NEW (_("Spell Checking"));
TABLE_NEW (1, 2);
button = katze_property_proxy (settings, "enable-spell-checking", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Enable Spell Checking"));
gtk_widget_set_tooltip_text (button, _("Enable spell checking while typing"));
INDENTED_ADD (button, 0, 1, 0, 1);
entry = katze_property_proxy (settings, "spell-checking-languages", NULL);
/* i18n: The example should be adjusted to contain a good local default */
gtk_widget_set_tooltip_text (entry, _("A comma separated list of languages to be used for spell checking, for example \"en_GB,de_DE\""));
FILLED_ADD (entry, 1, 2, 0, 1);
#endif
/* Page "Interface" */
PAGE_NEW (GTK_STOCK_CONVERT, _("Interface"));
@ -586,6 +569,53 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
button = katze_property_proxy (settings, "close-buttons-on-tabs", NULL);
WIDGET_ADD (button, 1, 2, 5, 6);
/* Page "Applications" */
PAGE_NEW (GTK_STOCK_CONVERT, _("Applications"));
FRAME_NEW (_("External applications"));
TABLE_NEW (3, 2);
label = katze_property_label (settings, "text-editor");
INDENTED_ADD (label, 0, 1, 0, 1);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "text-editor", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 0, 1);
label = katze_property_label (settings, "download-manager");
INDENTED_ADD (label, 0, 1, 1, 2);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "download-manager", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 1, 2);
label = katze_property_label (settings, "news-aggregator");
INDENTED_ADD (label, 0, 1, 2, 3);
hbox = gtk_hbox_new (FALSE, 4);
button = gtk_image_new ();
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
gtk_widget_set_size_request (button, icon_width, icon_height);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
entry = katze_property_proxy (settings, "news-aggregator", NULL);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
proxy_download_manager_icon_cb (entry, NULL, GTK_IMAGE (button));
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (proxy_download_manager_icon_cb), button);
FILLED_ADD (hbox, 1, 2, 2, 3);
/* Page "Network" */
PAGE_NEW (GTK_STOCK_NETWORK, _("Network"));
FRAME_NEW (_("Network"));

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2009 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Jean-François Guchens <zcx000@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -23,27 +24,27 @@
#include <string.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <webkit/webkit.h>
#if GLIB_CHECK_VERSION (2, 18, 0)
#define D_(__domain, __message) g_dgettext (__domain, __message)
#elif ENABLE_NLS
#define D_(__domain, __message) dgettext (__domain, __message)
#else
#define D_(__domain, __message) __message
#endif
/* This is unstable API, so we need to declare it */
gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view);
/* This is public API since WebKitGTK+ 1.1.6 */
#if !WEBKIT_CHECK_VERSION (1, 1, 6)
void
webkit_web_frame_print (WebKitWebFrame* web_frame);
#endif
GdkPixbuf*
midori_search_action_get_icon (KatzeNet* net,
KatzeItem* item,
GtkWidget* widget);
static void
midori_view_construct_web_view (MidoriView* view);
struct _MidoriView
{
GtkScrolledWindow parent_instance;
@ -60,8 +61,9 @@ struct _MidoriView
gchar* selected_text;
MidoriWebSettings* settings;
GtkWidget* web_view;
/* KatzeArray* news_feeds; */
KatzeArray* news_feeds;
gboolean speed_dial_in_new_tabs;
gchar* download_manager;
gchar* news_aggregator;
gboolean middle_click_opens_selection;
@ -135,7 +137,7 @@ enum
PROP_LOAD_STATUS,
PROP_PROGRESS,
PROP_ZOOM_LEVEL,
/* PROP_NEWS_FEEDS, */
PROP_NEWS_FEEDS,
PROP_STATUSBAR_TEXT,
PROP_SETTINGS,
PROP_NET
@ -153,6 +155,7 @@ enum {
SEARCH_TEXT,
ADD_BOOKMARK,
SAVE_AS,
ADD_SPEED_DIAL,
LAST_SIGNAL
};
@ -179,6 +182,16 @@ midori_view_settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
MidoriView* view);
static void
midori_view_speed_dial_get_thumb (GtkWidget* web_view,
const gchar* message,
MidoriView* view);
static void
midori_view_speed_dial_save (GtkWidget* web_view,
const gchar* message);
static void
midori_view_class_init (MidoriViewClass* class)
{
@ -354,6 +367,26 @@ midori_view_class_init (MidoriViewClass* class)
G_TYPE_NONE, 1,
G_TYPE_STRING);
/**
* MidoriView::add-speed-dial:
* @view: the object on which the signal is emitted
* @uri: the URI to add to the speed dial
*
* Emitted when an URI is added to the spee dial page.
*
* Since: 0.1.7
*/
signals[ADD_SPEED_DIAL] = g_signal_new (
"add-speed-dial",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = midori_view_finalize;
gobject_class->set_property = midori_view_set_property;
@ -434,14 +467,21 @@ midori_view_class_init (MidoriViewClass* class)
1.0f,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/* g_object_class_install_property (gobject_class,
/**
* MidoriView:news-feeds:
*
* The news feeds advertised by the currently loaded page.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_NEWS_FEEDS,
g_param_spec_object (
"news-feeds",
"News Feeds",
"The list of available news feeds",
KATZE_TYPE_ARRAY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); */
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_STATUSBAR_TEXT,
@ -471,6 +511,52 @@ midori_view_class_init (MidoriViewClass* class)
flags));
}
static void
midori_view_update_title (MidoriView* view)
{
/* If left-to-right text is combined with right-to-left text the default
behaviour of Pango can result in awkwardly aligned text. For example
"‪بستيان نوصر (hadess) | An era comes to an end - Midori" becomes
"hadess) | An era comes to an end - Midori) بستيان نوصر". So to prevent
this we insert an LRE character before the title which indicates that
we want left-to-right but retains the direction of right-to-left text. */
if (view->title && !g_str_has_prefix (view->title, ""))
{
gchar* new_title = g_strconcat ("", view->title, NULL);
katze_assign (view->title, new_title);
}
#define title midori_view_get_display_title (view)
if (view->tab_label)
{
/* If the title starts with the presumed name of the website, we
ellipsize differently, to emphasize the subtitle */
if (gtk_label_get_angle (GTK_LABEL (view->tab_title)) == 0.0)
{
SoupURI* uri = soup_uri_new (view->uri);
const gchar* host = uri ? (uri->host ? uri->host : "") : "";
const gchar* name = g_str_has_prefix (host, "www.") ? &host[4] : host;
guint i = 0;
while (name[i++])
if (name[i] == '.')
break;
if (!g_ascii_strncasecmp (title, name, i))
gtk_label_set_ellipsize (GTK_LABEL (view->tab_title), PANGO_ELLIPSIZE_START);
else
gtk_label_set_ellipsize (GTK_LABEL (view->tab_title), PANGO_ELLIPSIZE_END);
if (uri)
soup_uri_free (uri);
}
gtk_label_set_text (GTK_LABEL (view->tab_title), title);
gtk_widget_set_tooltip_text (view->tab_title, title);
}
if (view->menu_item)
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (
view->menu_item))), title);
if (view->item)
katze_item_set_name (view->item, title);
#undef title
}
static GdkPixbuf*
midori_view_mime_icon (GtkIconTheme* icon_theme,
const gchar* format,
@ -502,7 +588,10 @@ midori_view_update_icon (MidoriView* view,
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if ((parts = g_strsplit (view->mime_type, "/", 2)))
parts = (parts[0] && parts[1]) ? parts : NULL;
{
if (!(parts[0] && parts[1]))
katze_assign (parts, NULL);
}
}
else
parts = NULL;
@ -623,6 +712,55 @@ webkit_web_view_progress_changed_cb (WebKitWebView* web_view,
g_object_notify (G_OBJECT (view), "progress");
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
static gboolean
webkit_web_view_load_error_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
const gchar* uri,
GError* error,
MidoriView* view)
{
const gchar* template_file = DATADIR "/midori/res/error.html";
gchar* template;
if (g_file_get_contents (template_file, &template, NULL, NULL))
{
SoupServer* res_server;
guint port;
gchar* res_root;
gchar* stock_root;
gchar* message;
gchar* result;
res_server = sokoke_get_res_server ();
port = soup_server_get_port (res_server);
res_root = g_strdup_printf ("http://localhost:%d/res", port);
stock_root = g_strdup_printf ("http://localhost:%d/stock", port);
message = g_strdup_printf (_("The page '%s' couldn't be loaded."), uri);
result = sokoke_replace_variables (template,
"{title}", _("Error"),
"{message}", message,
"{description}", error->message,
"{tryagain}", _("Try again"),
"{res}", res_root,
"{stock}", stock_root,
NULL);
g_free (template);
g_free (message);
webkit_web_frame_load_alternate_string (web_frame,
result, res_root, uri);
g_free (res_root);
g_free (stock_root);
g_free (result);
return TRUE;
}
return FALSE;
}
#else
static void
webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame,
gboolean success,
@ -653,6 +791,7 @@ webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame,
midori_view_update_load_status (view, MIDORI_LOAD_FINISHED);
}
#endif
static void
webkit_web_view_load_finished_cb (WebKitWebView* web_view,
@ -668,14 +807,34 @@ webkit_web_view_load_finished_cb (WebKitWebView* web_view,
if (view->news_aggregator && *view->news_aggregator)
{
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
/* This snippet joins the available news feeds into a string like this:
URI1|title1,URI2|title2
FIXME: Ensure separators contained in the string can't break it */
gchar* value = sokoke_js_script_eval (js_context,
"function feeds (l) { var f = new Array (); for (i in l) "
"{ var t = l[i].type; "
"if (t && (t.indexOf ('rss') != -1 || t.indexOf ('atom') != -1)) "
"f.push (l[i].href); } return f; }"
"f.push (l[i].href + '|' + l[i].title); } return f; }"
"feeds (document.getElementsByTagName ('link'))", NULL);
gchar** items = g_strsplit (value, ",", 0);
gchar** iter;
katze_array_clear (view->news_feeds);
for (iter = items; iter && *iter; iter++)
{
gchar** parts = g_strsplit (*iter, "|", 2);
KatzeItem* item = g_object_new (KATZE_TYPE_ITEM,
"uri", parts ? *parts : "",
"name", parts && *parts ? parts[1] : NULL,
NULL);
katze_array_add_item (view->news_feeds, item);
g_object_unref (item);
g_strfreev (parts);
}
g_strfreev (items);
g_object_set_data (G_OBJECT (view), "news-feeds",
value && *value ? (void*)1 : (void*)0);
g_free (value);
/* Ensure load-status is notified again, whether it changed or not */
g_object_notify (G_OBJECT (view), "load-status");
}
@ -683,6 +842,26 @@ webkit_web_view_load_finished_cb (WebKitWebView* web_view,
g_object_thaw_notify (G_OBJECT (view));
}
#if WEBKIT_CHECK_VERSION (1, 1, 4)
static void
webkit_web_view_notify_uri_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
g_object_get (web_view, "uri", &view->uri, NULL);
g_object_notify (G_OBJECT (view), "uri");
}
static void
webkit_web_view_notify_title_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
g_object_get (web_view, "title", &view->title, NULL);
midori_view_update_title (view);
g_object_notify (G_OBJECT (view), "title");
}
#else
static void
webkit_web_view_title_changed_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
@ -691,6 +870,7 @@ webkit_web_view_title_changed_cb (WebKitWebView* web_view,
{
g_object_set (view, "title", title, NULL);
}
#endif
static void
webkit_web_view_statusbar_text_changed_cb (WebKitWebView* web_view,
@ -814,6 +994,17 @@ gtk_widget_button_press_event_cb (WebKitWebView* web_view,
case 9:
midori_view_go_forward (view);
return TRUE;
/*
* On some fancier mice the scroll wheel can be used to scroll horizontally.
* A middle click usually registers both a middle click (2) and a
* horizontal scroll (11 or 12).
* We catch horizontal scrolls and ignore them to prevent middle clicks from
* accidentally being interpreted as first button clicks.
*/
case 11:
return TRUE;
case 12:
return TRUE;
}
return FALSE;
@ -829,10 +1020,15 @@ gtk_widget_key_press_event_cb (WebKitWebView* web_view,
if (character == (event->keyval | 0x01000000))
return FALSE;
if (view->find_while_typing && !webkit_web_view_can_cut_clipboard (web_view)
if (character == '.' || character == '/')
character = '\0';
else if (!view->find_while_typing)
return FALSE;
if (!webkit_web_view_can_cut_clipboard (web_view)
&& !webkit_web_view_can_paste_clipboard (web_view))
{
gchar* text = g_strdup_printf ("%c", character);
gchar* text = character ? g_strdup_printf ("%c", character) : g_strdup ("");
g_signal_emit (view, signals[SEARCH_TEXT], 0, TRUE, text);
g_free (text);
@ -875,10 +1071,10 @@ midori_web_view_menu_new_tab_activate_cb (GtkWidget* widget,
}
static void
midori_web_view_menu_new_window_activate_cb (GtkWidget* widget,
MidoriView* view)
midori_web_view_menu_action_add_speed_dial_cb (GtkWidget* widget,
MidoriView* view)
{
g_signal_emit (view, signals[NEW_WINDOW], 0, view->link_uri);
g_signal_emit (view, signals[ADD_SPEED_DIAL], 0, view->link_uri);
}
static void
@ -893,10 +1089,7 @@ midori_web_view_menu_search_web_activate_cb (GtkWidget* widget,
else
g_object_get (view->settings, "location-entry-search",
&search, NULL);
if (strstr (search, "%s"))
uri = g_strdup_printf (search, view->selected_text);
else
uri = g_strconcat (search, view->selected_text, NULL);
uri = sokoke_search_uri (search, view->selected_text);
g_free (search);
g_signal_emit (view, signals[NEW_TAB], 0, uri,
@ -918,7 +1111,7 @@ static void
midori_web_view_menu_download_activate_cb (GtkWidget* widget,
MidoriView* view)
{
sokoke_spawn_program (view->download_manager, view->link_uri);
sokoke_spawn_program (view->download_manager, view->link_uri, TRUE);
}
static void
@ -993,9 +1186,6 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
/* hack to localize menu item */
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), _("Open Link in New _Window"));
/* hack to implement New Window */
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_new_window_activate_cb), view);
menuitem = (GtkWidget*)g_list_nth_data (items, 3);
g_list_free (items);
#if WEBKIT_CHECK_VERSION (1, 1, 3)
@ -1103,8 +1293,12 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
/* hack to localize menu item */
if (GTK_IS_BIN (menuitem))
{
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), D_("gtk20", "_Refresh"));
GtkStockItem stock_item;
if (gtk_stock_lookup (GTK_STOCK_REFRESH, &stock_item))
{
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), stock_item.label);
}
}
g_list_free (items);
menuitem = gtk_image_menu_item_new_with_mnemonic (_("Undo Close Tab"));
@ -1116,15 +1310,28 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
G_CALLBACK (midori_web_view_menu_action_activate_cb), view);
/* FIXME: Make this sensitive only when there is a tab to undo */
gtk_widget_show (menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_image_menu_item_new_from_stock (STOCK_BOOKMARK_ADD, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "BookmarkAdd");
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_action_activate_cb), view);
gtk_widget_show (menuitem);
if (view->speed_dial_in_new_tabs && !midori_view_is_blank (view))
{
menuitem = gtk_image_menu_item_new_with_mnemonic (_("Add to Speed _dial"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "AddSpeedDial");
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_action_add_speed_dial_cb), view);
gtk_widget_show (menuitem);
}
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "SaveAs");
@ -1135,6 +1342,7 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
saving either. If that changes, we need to think of something. */
if (!midori_view_can_view_source (view))
gtk_widget_set_sensitive (menuitem, FALSE);
menuitem = gtk_image_menu_item_new_with_mnemonic (_("View _Source"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "SourceView");
@ -1143,6 +1351,7 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
gtk_widget_show (menuitem);
if (!midori_view_can_view_source (view))
gtk_widget_set_sensitive (menuitem, FALSE);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "Print");
@ -1187,7 +1396,7 @@ webkit_web_view_create_web_view_cb (GtkWidget* web_view,
"net", view->net,
"settings", view->settings,
NULL);
midori_view_set_uri (MIDORI_VIEW (new_view), "");
midori_view_construct_web_view (MIDORI_VIEW (new_view));
g_signal_connect (MIDORI_VIEW (new_view)->web_view, "web-view-ready",
G_CALLBACK (webkit_web_view_web_view_ready_cb), view);
return MIDORI_VIEW (new_view)->web_view;
@ -1322,14 +1531,20 @@ webkit_web_view_download_requested_cb (GtkWidget* web_view,
}
#endif
static void
static gboolean
webkit_web_view_console_message_cb (GtkWidget* web_view,
const gchar* message,
guint line,
const gchar* source_id,
MidoriView* view)
{
g_signal_emit (view, signals[CONSOLE_MESSAGE], 0, message, line, source_id);
if (!strncmp (message, "speed_dial-get-thumbnail", 22))
midori_view_speed_dial_get_thumb (web_view, message, view);
else if (!strncmp (message, "speed_dial-save", 13))
midori_view_speed_dial_save (web_view, message);
else
g_signal_emit (view, signals[CONSOLE_MESSAGE], 0, message, line, source_id);
return TRUE;
}
static void
@ -1355,6 +1570,8 @@ midori_view_init (MidoriView* view)
view->statusbar_text = NULL;
view->link_uri = NULL;
view->selected_text = NULL;
view->news_feeds = katze_array_new (KATZE_TYPE_ITEM);
view->item = NULL;
view->download_manager = NULL;
@ -1382,6 +1599,7 @@ midori_view_finalize (GObject* object)
katze_assign (view->statusbar_text, NULL);
katze_assign (view->link_uri, NULL);
katze_assign (view->selected_text, NULL);
katze_object_assign (view->news_feeds, NULL);
katze_object_assign (view->settings, NULL);
katze_object_assign (view->item, NULL);
@ -1408,18 +1626,7 @@ midori_view_set_property (GObject* object,
{
case PROP_TITLE:
katze_assign (view->title, g_value_dup_string (value));
#define title midori_view_get_display_title (view)
if (view->tab_label)
{
gtk_label_set_text (GTK_LABEL (view->tab_title), title);
gtk_widget_set_tooltip_text (view->tab_title, title);
}
if (view->menu_item)
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (
view->menu_item))), title);
if (view->item)
katze_item_set_name (view->item, title);
#undef title
midori_view_update_title (view);
break;
case PROP_ZOOM_LEVEL:
midori_view_set_zoom_level (view, g_value_get_float (value));
@ -1472,6 +1679,9 @@ midori_view_get_property (GObject* object,
case PROP_ZOOM_LEVEL:
g_value_set_float (value, midori_view_get_zoom_level (view));
break;
case PROP_NEWS_FEEDS:
g_value_set_object (value, view->news_feeds);
break;
case PROP_STATUSBAR_TEXT:
g_value_set_string (value, view->statusbar_text);
break;
@ -1509,6 +1719,7 @@ _midori_view_update_settings (MidoriView* view)
gboolean zoom_text_and_images;
g_object_get (view->settings,
"speed-dial-in-new-tabs", &view->speed_dial_in_new_tabs,
"download-manager", &view->download_manager,
"news-aggregator", &view->news_aggregator,
"zoom-text-and-images", &zoom_text_and_images,
@ -1536,7 +1747,11 @@ midori_view_settings_notify_cb (MidoriWebSettings* settings,
g_value_init (&value, pspec->value_type);
g_object_get_property (G_OBJECT (view->settings), name, &value);
if (name == g_intern_string ("download-manager"))
if (name == g_intern_string ("speed-dial-in-new-tabs"))
{
view->speed_dial_in_new_tabs = g_value_get_boolean (&value);
}
else if (name == g_intern_string ("download-manager"))
{
katze_assign (view->download_manager, g_value_dup_string (&value));
}
@ -1710,6 +1925,8 @@ midori_view_construct_web_view (MidoriView* view)
WebKitWebFrame* web_frame;
gpointer inspector;
g_return_if_fail (!view->web_view);
view->web_view = webkit_web_view_new ();
/* Load something to avoid a bug where WebKit might not set a main frame */
@ -1725,15 +1942,22 @@ midori_view_construct_web_view (MidoriView* view)
webkit_web_view_progress_changed_cb, view,
"signal::load-finished",
webkit_web_view_load_finished_cb, view,
#if WEBKIT_CHECK_VERSION (1, 1, 4)
"signal::notify::uri",
webkit_web_view_notify_uri_cb, view,
"signal::notify::title",
webkit_web_view_notify_title_cb, view,
#else
"signal::title-changed",
webkit_web_view_title_changed_cb, view,
#endif
"signal::status-bar-text-changed",
webkit_web_view_statusbar_text_changed_cb, view,
"signal::hovering-over-link",
webkit_web_view_hovering_over_link_cb, view,
"signal::button-press-event",
gtk_widget_button_press_event_cb, view,
"signal::key-press-event",
"signal-after::key-press-event",
gtk_widget_key_press_event_cb, view,
"signal::scroll-event",
gtk_widget_scroll_event_cb, view,
@ -1751,12 +1975,18 @@ midori_view_construct_web_view (MidoriView* view)
"signal::download-requested",
webkit_web_view_download_requested_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 6)
"signal::load-error",
webkit_web_view_load_error_cb, view,
#endif
NULL);
#if !WEBKIT_CHECK_VERSION (1, 1, 6)
g_object_connect (web_frame,
"signal::load-done",
webkit_web_frame_load_done_cb, view,
NULL);
#endif
if (view->settings)
{
@ -1787,17 +2017,81 @@ midori_view_set_uri (MidoriView* view,
g_return_if_fail (MIDORI_IS_VIEW (view));
/* Treat "about:blank" and "" equally, see midori_view_is_blank(). */
if (!g_strcmp0 (uri, "about:blank")) uri = "";
if (!uri) uri = "";
if (1)
{
if (!view->web_view)
midori_view_construct_web_view (view);
if (view->speed_dial_in_new_tabs && !g_strcmp0 (uri, ""))
{
SoupServer* res_server;
guint port;
gchar* res_root;
gchar* speed_dial_head;
gchar* speed_dial_body;
gchar* body_fname;
gchar* stock_root;
katze_assign (view->uri, g_strdup (""));
g_file_get_contents (DATADIR "/midori/res/speeddial-head.html",
&speed_dial_head, NULL, NULL);
res_server = sokoke_get_res_server ();
port = soup_server_get_port (res_server);
res_root = g_strdup_printf ("http://localhost:%d/res", port);
stock_root = g_strdup_printf ("http://localhost:%d/stock", port);
body_fname = g_build_filename (sokoke_set_config_dir (NULL),
"speeddial.json", NULL);
if (!g_file_test (body_fname, G_FILE_TEST_EXISTS))
{
if (g_file_get_contents (DATADIR "/midori/res/speeddial.json",
&speed_dial_body, NULL, NULL))
g_file_set_contents (body_fname, speed_dial_body, -1, NULL);
else
speed_dial_body = g_strdup ("");
}
else
g_file_get_contents (body_fname, &speed_dial_body, NULL, NULL);
data = sokoke_replace_variables (speed_dial_head,
"{res}", res_root,
"{stock}", stock_root,
"{json_data}", speed_dial_body,
"{title}", _("Speed dial"),
"{click_to_add}", _("Click to add a shortcut"),
"{enter_shortcut_address}", _("Enter shortcut address"),
"{enter_shortcut_name}", _("Enter shortcut title"),
"{are_you_sure}", _("Are you sure you want to delete this shortcut?"), NULL);
#if WEBKIT_CHECK_VERSION (1, 1, 6)
webkit_web_frame_load_alternate_string (
webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view)),
data, res_root, "about:blank");
#else
webkit_web_view_load_html_string (
WEBKIT_WEB_VIEW (view->web_view), data, res_root);
#endif
g_free (res_root);
g_free (stock_root);
g_free (data);
g_free (speed_dial_head);
g_free (speed_dial_body);
g_free (body_fname);
}
/* This is not prefectly elegant, but creating an
error page inline is the simplest solution. */
if (g_str_has_prefix (uri, "error:"))
else if (g_str_has_prefix (uri, "error:"))
{
data = NULL;
#if !WEBKIT_CHECK_VERSION (1, 1, 3)
if (!strncmp (uri, "error:nodisplay ", 16))
{
gchar* title;
@ -1814,7 +2108,8 @@ midori_view_set_uri (MidoriView* view,
title, title, view->uri, view->mime_type);
g_free (title);
}
else if (!strncmp (uri, "error:nodocs ", 13))
#endif
if (!strncmp (uri, "error:nodocs ", 13))
{
gchar* title;
@ -1850,13 +2145,7 @@ midori_view_set_uri (MidoriView* view,
}
else if (g_str_has_prefix (uri, "mailto:"))
{
if (!gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL))
{
/* Fallback to Exo for example if GConf isn't setup */
gchar* command = g_strconcat ("exo-open ", uri, NULL);
g_spawn_command_line_async (command, NULL);
g_free (command);
}
sokoke_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL);
}
else
{
@ -1945,11 +2234,10 @@ midori_view_get_display_title (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), "about:blank");
if (midori_view_is_blank (view))
return _("Blank page");
if (view->title && *view->title)
return view->title;
if (midori_view_is_blank (view))
return _("Blank page");
return midori_view_get_display_uri (view);
}
@ -2154,7 +2442,8 @@ midori_view_update_tab_title (GtkWidget* label,
if (angle == 0.0 || angle == 360.0)
{
gtk_widget_set_size_request (label, width * size, -1);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
if (gtk_label_get_ellipsize (GTK_LABEL (label)) != PANGO_ELLIPSIZE_START)
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
}
else
{
@ -2468,9 +2757,15 @@ midori_view_reload (MidoriView* view,
g_return_if_fail (MIDORI_IS_VIEW (view));
#if WEBKIT_CHECK_VERSION (1, 1, 6)
/* WebKit 1.1.6 doesn't handle "alternate content" flawlessly,
so reloading via Javascript works but not via API calls. */
title = g_strdup (_("Error"));
#else
/* Error pages are special, we want to try loading the destination
again, not the error page which isn't even a proper page */
title = g_strdup_printf (_("Not found - %s"), view->uri);
#endif
if (view->title && strstr (title, view->title))
webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), view->uri);
else if (from_cache)
@ -2679,3 +2974,193 @@ midori_view_execute_script (MidoriView* view,
webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view->web_view), script);
return TRUE;
}
/* For now this is private API */
GdkPixbuf*
midori_view_get_snapshot (MidoriView* view,
guint width,
guint height)
{
GtkWidget* web_view;
GdkRectangle rect;
GdkPixmap* pixmap;
GdkEvent event;
gboolean result;
GdkColormap* colormap;
GdkPixbuf* pixbuf;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
web_view = gtk_bin_get_child (GTK_BIN (view));
g_return_val_if_fail (web_view->window, NULL);
rect.x = web_view->allocation.x;
rect.y = web_view->allocation.y;
rect.width = web_view->allocation.width;
rect.height = web_view->allocation.height;
pixmap = gdk_pixmap_new (web_view->window,
web_view->allocation.width, web_view->allocation.height,
gdk_drawable_get_depth (web_view->window));
event.expose.type = GDK_EXPOSE;
event.expose.window = pixmap;
event.expose.send_event = FALSE;
event.expose.count = 0;
event.expose.area.x = 0;
event.expose.area.y = 0;
gdk_drawable_get_size (GDK_DRAWABLE (web_view->window),
&event.expose.area.width, &event.expose.area.height);
event.expose.region = gdk_region_rectangle (&event.expose.area);
g_signal_emit_by_name (web_view, "expose-event", &event, &result);
colormap = gdk_drawable_get_colormap (pixmap);
pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, colormap, 0, 0,
0, 0, rect.width, rect.height);
g_object_unref (pixmap);
if (width || height)
{
GdkPixbuf* scaled;
if (!width)
width = rect.width;
if (!height)
height = rect.height;
scaled = gdk_pixbuf_scale_simple (pixbuf, width, height,
GDK_INTERP_TILES);
g_object_unref (pixbuf);
return scaled;
}
return pixbuf;
}
static void
thumb_view_load_status_cb (MidoriView* thumb_view,
GParamSpec* pspec,
MidoriView* view)
{
GdkPixbuf* img;
gchar* file_content;
gchar* encoded;
gchar* dom_id;
gchar* js;
gsize sz;
if (katze_object_get_enum (thumb_view, "load-status") != MIDORI_LOAD_FINISHED)
return;
img = midori_view_get_snapshot (MIDORI_VIEW (thumb_view), 160, 107);
gdk_pixbuf_save_to_buffer (img, &file_content, &sz, "png", NULL, "compression", "7", NULL);
encoded = g_base64_encode ((guchar *)file_content, sz );
/* Call Javascript function to replace shortcut's content */
dom_id = g_object_get_data (G_OBJECT (thumb_view), "dom-id");
js = g_strdup_printf ("setThumbnail('%s','%s','%s');",
dom_id, encoded, thumb_view->uri);
webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view->web_view), js);
free (js);
g_object_unref (img);
g_free (dom_id);
g_free (encoded);
g_free (file_content);
gtk_widget_destroy (GTK_WIDGET (thumb_view));
}
/**
* midori_view_speed_dial_inject_thumb
* @view: a #MidoriView
* @filename: filename of the thumbnail
* @dom_id: Id of the shortcut on speed_dial page in wich to inject content
* @url: url of the shortcut
*/
static void
midori_view_speed_dial_inject_thumb (MidoriView* view,
gchar* filename,
gchar* dom_id,
gchar* url)
{
GtkWidget* thumb_view;
MidoriWebSettings* settings;
GtkWidget* browser;
GtkWidget* notebook;
GtkWidget* label;
thumb_view = midori_view_new (view->net);
settings = g_object_new (MIDORI_TYPE_WEB_SETTINGS, "enable-scripts", FALSE,
"enable-plugins", FALSE, "auto-load-images", TRUE, NULL);
midori_view_set_settings (MIDORI_VIEW (thumb_view), settings);
browser = gtk_widget_get_toplevel (GTK_WIDGET (view));
if (!GTK_IS_WINDOW (browser))
return;
/* What we are doing here is a bit of a hack. In order to render a
thumbnail we need a new view and load the url in it. But it has
to be visible and packed in a container. So we secretly pack it
into the notebook of the parent browser. */
notebook = katze_object_get_object (browser, "notebook");
if (!notebook)
return;
gtk_container_add (GTK_CONTAINER (notebook), thumb_view);
/* We use an empty label. It's not invisible but at least hard to spot. */
label = gtk_event_box_new ();
gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), thumb_view, label);
g_object_unref (notebook);
gtk_widget_show (thumb_view);
g_object_set_data (G_OBJECT (thumb_view), "dom-id", dom_id);
g_signal_connect (thumb_view, "notify::load-status",
G_CALLBACK (thumb_view_load_status_cb), view);
midori_view_set_uri (MIDORI_VIEW (thumb_view), url);
}
/**
* midori_view_speed_dial_save
* @web_view: a #WebkitView
* @message: Console log data
*
* Load a thumbnail, and set the DOM
*
* message[0] == console message call
* message[1] == shortcut id in the DOM
* message[2] == shortcut uri
*
**/
static void
midori_view_speed_dial_get_thumb (GtkWidget* web_view,
const gchar* message,
MidoriView* view)
{
gchar** t_data = g_strsplit (message," ", 4);
if (t_data[1] == NULL || t_data[2] == NULL )
return;
midori_view_speed_dial_inject_thumb (view, NULL,
g_strdup (t_data[1]), g_strdup (t_data[2]));
g_strfreev (t_data);
}
/**
* midori_view_speed_dial_save
* @web_view: a #WebkitView
*
* Save speed_dial DOM structure to body template
*
**/
static void
midori_view_speed_dial_save (GtkWidget* web_view,
const gchar* message)
{
gchar* json = g_strdup (message + 15);
gchar* fname = g_build_filename (sokoke_set_config_dir (NULL),
"speeddial.json", NULL);
GRegex* reg_double = g_regex_new ("\\\\\"", 0, 0, NULL);
gchar* safe = g_regex_replace_literal (reg_double, json, -1, 0, "\\\\\"", 0, NULL);
g_file_set_contents (fname, safe, -1, NULL);
g_free (fname);
g_free (json);
g_free (safe);
g_regex_unref (reg_double);
}

View file

@ -49,7 +49,10 @@ struct _MidoriWebSettings
MidoriStartup load_on_startup;
gchar* homepage;
gboolean show_crash_dialog;
gboolean speed_dial_in_new_tabs;
gchar* download_folder;
gboolean ask_for_destination_folder;
gboolean notify_transfer_completed;
gchar* download_manager;
gchar* text_editor;
gchar* news_aggregator;
@ -81,6 +84,8 @@ struct _MidoriWebSettings
MidoriIdentity identify_as;
gchar* ident_string;
gint cache_size;
gint clear_private_data;
};
struct _MidoriWebSettingsClass
@ -119,7 +124,10 @@ enum
PROP_LOAD_ON_STARTUP,
PROP_HOMEPAGE,
PROP_SHOW_CRASH_DIALOG,
PROP_SPEED_DIAL_IN_NEW_TABS,
PROP_DOWNLOAD_FOLDER,
PROP_ASK_FOR_DESTINATION_FOLDER,
PROP_NOTIFY_TRANSFER_COMPLETED,
PROP_DOWNLOAD_MANAGER,
PROP_TEXT_EDITOR,
PROP_NEWS_AGGREGATOR,
@ -135,8 +143,6 @@ enum
PROP_OPEN_TABS_NEXT_TO_CURRENT,
PROP_OPEN_POPUPS_IN_TABS,
PROP_ENFORCE_96_DPI,
PROP_ENABLE_DEVELOPER_EXTRAS,
PROP_ZOOM_TEXT_AND_IMAGES,
PROP_FIND_WHILE_TYPING,
PROP_ACCEPT_COOKIES,
@ -152,7 +158,9 @@ enum
PROP_AUTO_DETECT_PROXY,
PROP_IDENTIFY_AS,
PROP_IDENT_STRING,
PROP_CACHE_SIZE
PROP_CACHE_SIZE,
PROP_CLEAR_PRIVATE_DATA
};
GType
@ -541,7 +549,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("Load on Startup"),
_("What to load on startup"),
MIDORI_TYPE_STARTUP,
MIDORI_STARTUP_HOMEPAGE,
MIDORI_STARTUP_LAST_OPEN_PAGES,
flags));
g_object_class_install_property (gobject_class,
@ -569,6 +577,27 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
TRUE,
flags));
/**
* MidoriWebSettings:speed-dial-in-new-tabs:
*
* Show spee dial in newly opened tabs.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_SPEED_DIAL_IN_NEW_TABS,
g_param_spec_boolean (
"speed-dial-in-new-tabs",
/* i18n: Speed dial, webpage shortcuts, named for the phone function */
_("Show speed dial in new tabs"),
_("Show speed dial in newly opened tabs"),
TRUE,
#if GTK_CHECK_VERSION (2, 14, 0)
flags));
#else
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif
g_object_class_install_property (gobject_class,
PROP_DOWNLOAD_FOLDER,
g_param_spec_string (
@ -582,6 +611,46 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif
/**
* MidoriWebSettings:ask-for-destination-folder:
*
* Whether to ask for the destination folder when downloading a file.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_ASK_FOR_DESTINATION_FOLDER,
g_param_spec_boolean (
"ask-for-destination-folder",
_("Ask for the destination folder"),
_("Whether to ask for the destination folder when downloading a file"),
FALSE,
#if WEBKIT_CHECK_VERSION (1, 1, 3)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#else
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif
/**
* MidoriWebSettings:notify-transfer-completed:
*
* Whether to show a notification when a transfer has been completed.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_NOTIFY_TRANSFER_COMPLETED,
g_param_spec_boolean (
"notify-transfer-completed",
_("Notify when a transfer has been completed"),
_("Whether to show a notification when a transfer has been completed"),
TRUE,
#if WEBKIT_CHECK_VERSION (1, 1, 3)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#else
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif
g_object_class_install_property (gobject_class,
PROP_DOWNLOAD_MANAGER,
g_param_spec_string (
@ -701,7 +770,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
g_param_spec_boolean (
"open-tabs-next-to-current",
_("Open Tabs next to Current"),
_("Whether to open new tabs next to the current tab or after the last one"),
_("Whether to open new tabs next to the current tab or after the last one"),
TRUE,
flags));
@ -882,6 +951,23 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("The allowed size of the cache"),
0, G_MAXINT, 100,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* MidoriWebSettings:clear-private-data:
*
* The private data selected for deletion.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_CLEAR_PRIVATE_DATA,
g_param_spec_int (
"clear-private-data",
_("Clear private data"),
_("The private data selected for deletion"),
0, G_MAXINT, 0,
flags));
}
static void
@ -889,7 +975,7 @@ notify_default_encoding_cb (GObject* object,
GParamSpec* pspec)
{
MidoriWebSettings* web_settings;
const gchar* string;
gchar* string;
const gchar* encoding;
web_settings = MIDORI_WEB_SETTINGS (object);
@ -908,12 +994,14 @@ notify_default_encoding_cb (GObject* object,
web_settings->preferred_encoding = MIDORI_ENCODING_WESTERN;
else
web_settings->preferred_encoding = MIDORI_ENCODING_CUSTOM;
g_free (string);
g_object_notify (object, "preferred-encoding");
}
static void
midori_web_settings_init (MidoriWebSettings* web_settings)
{
web_settings->notify_transfer_completed = TRUE;
web_settings->download_folder = g_strdup (midori_get_download_dir ());
web_settings->http_proxy = NULL;
web_settings->open_popups_in_tabs = TRUE;
@ -928,6 +1016,20 @@ midori_web_settings_init (MidoriWebSettings* web_settings)
static void
midori_web_settings_finalize (GObject* object)
{
MidoriWebSettings* web_settings;
web_settings = MIDORI_WEB_SETTINGS (object);
katze_assign (web_settings->toolbar_items, NULL);
katze_assign (web_settings->homepage, NULL);
katze_assign (web_settings->download_folder, NULL);
katze_assign (web_settings->download_manager, NULL);
katze_assign (web_settings->text_editor, NULL);
katze_assign (web_settings->news_aggregator, NULL);
katze_assign (web_settings->location_entry_search, NULL);
katze_assign (web_settings->http_proxy, NULL);
katze_assign (web_settings->ident_string, NULL);
G_OBJECT_CLASS (midori_web_settings_parent_class)->finalize (object);
}
@ -1076,9 +1178,18 @@ midori_web_settings_set_property (GObject* object,
case PROP_SHOW_CRASH_DIALOG:
web_settings->show_crash_dialog = g_value_get_boolean (value);
break;
case PROP_SPEED_DIAL_IN_NEW_TABS:
web_settings->speed_dial_in_new_tabs = g_value_get_boolean (value);
break;
case PROP_DOWNLOAD_FOLDER:
katze_assign (web_settings->download_folder, g_value_dup_string (value));
break;
case PROP_ASK_FOR_DESTINATION_FOLDER:
web_settings->ask_for_destination_folder = g_value_get_boolean (value);
break;
case PROP_NOTIFY_TRANSFER_COMPLETED:
web_settings->notify_transfer_completed = g_value_get_boolean (value);
break;
case PROP_DOWNLOAD_MANAGER:
katze_assign (web_settings->download_manager, g_value_dup_string (value));
break;
@ -1190,6 +1301,9 @@ midori_web_settings_set_property (GObject* object,
case PROP_CACHE_SIZE:
web_settings->cache_size = g_value_get_int (value);
break;
case PROP_CLEAR_PRIVATE_DATA:
web_settings->clear_private_data = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1275,9 +1389,18 @@ midori_web_settings_get_property (GObject* object,
case PROP_SHOW_CRASH_DIALOG:
g_value_set_boolean (value, web_settings->show_crash_dialog);
break;
case PROP_SPEED_DIAL_IN_NEW_TABS:
g_value_set_boolean (value, web_settings->speed_dial_in_new_tabs);
break;
case PROP_DOWNLOAD_FOLDER:
g_value_set_string (value, web_settings->download_folder);
break;
case PROP_ASK_FOR_DESTINATION_FOLDER:
g_value_set_boolean (value, web_settings->ask_for_destination_folder);
break;
case PROP_NOTIFY_TRANSFER_COMPLETED:
g_value_set_boolean (value, web_settings->notify_transfer_completed);
break;
case PROP_DOWNLOAD_MANAGER:
g_value_set_string (value, web_settings->download_manager);
break;
@ -1319,12 +1442,6 @@ midori_web_settings_get_property (GObject* object,
g_value_set_boolean (value, web_settings->open_popups_in_tabs);
break;
case PROP_ENFORCE_96_DPI:
g_value_set_boolean (value, FALSE);
break;
case PROP_ENABLE_DEVELOPER_EXTRAS:
g_value_set_boolean (value, FALSE);
break;
case PROP_ZOOM_TEXT_AND_IMAGES:
g_value_set_boolean (value, web_settings->zoom_text_and_images);
break;
@ -1374,6 +1491,9 @@ midori_web_settings_get_property (GObject* object,
case PROP_CACHE_SIZE:
g_value_set_int (value, web_settings->cache_size);
break;
case PROP_CLEAR_PRIVATE_DATA:
g_value_set_int (value, web_settings->clear_private_data);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2009 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -12,6 +13,8 @@
#include "sokoke.h"
#include "midori-stock.h"
#include "compat.h"
#if HAVE_CONFIG_H
#include <config.h>
#endif
@ -93,9 +96,53 @@ error_dialog (const gchar* short_message,
}
/**
* sokoke_show_uri:
* @screen: a #GdkScreen, or %NULL
* @uri: the URI to show
* @timestamp: the timestamp of the event
* @error: the location of a #GError, or %NULL
*
* Shows the specified URI with an appropriate application. This
* supports xdg-open, exo-open and gnome-open as fallbacks if
* GIO doesn't do the trick.
*
* Return value: %TRUE on success, %FALSE if an error occurred
**/
gboolean
sokoke_show_uri (GdkScreen* screen,
const gchar* uri,
guint32 timestamp,
GError** error)
{
const gchar* fallbacks [] = { "xdg-open", "exo-open", "gnome-open" };
gsize i;
g_return_val_if_fail (GDK_IS_SCREEN (screen) || !screen, FALSE);
g_return_val_if_fail (uri != NULL, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
if (gtk_show_uri (screen, uri, timestamp, error))
return TRUE;
for (i = 0; i < G_N_ELEMENTS (fallbacks); i++)
{
gchar* command = g_strconcat (fallbacks[i], " ", uri, NULL);
gboolean result = g_spawn_command_line_async (command, error);
g_free (command);
if (result)
return TRUE;
if (error)
*error = NULL;
}
return FALSE;
}
gboolean
sokoke_spawn_program (const gchar* command,
const gchar* argument)
const gchar* argument,
gboolean quote)
{
gchar* argument_escaped;
gchar* command_ready;
@ -105,7 +152,7 @@ sokoke_spawn_program (const gchar* command,
g_return_val_if_fail (command != NULL, FALSE);
g_return_val_if_fail (argument != NULL, FALSE);
argument_escaped = g_shell_quote (argument);
argument_escaped = quote ? g_shell_quote (argument) : g_strdup (argument);
if (strstr (command, "%s"))
command_ready = g_strdup_printf (command, argument_escaped);
else
@ -175,6 +222,8 @@ sokoke_idn_to_punycode (gchar* uri)
g_utf8_strncpy (buffer, hostname, offset);
hostname = buffer;
}
else
hostname = g_strdup (hostname);
}
else
hostname = g_strdup (uri);
@ -213,6 +262,36 @@ sokoke_idn_to_punycode (gchar* uri)
#endif
}
/**
* sokoke_search_uri:
* @uri: a search URI with or without %s
* @keywords: keywords
*
* Takes a search engine URI and inserts the specified
* keywords. The @keywords are percent encoded. If the
* search URI contains a %s they keywords are inserted
* in that place, otherwise appended to the URI.
*
* Return value: a newly allocated search URI
**/
gchar* sokoke_search_uri (const gchar* uri,
const gchar* keywords)
{
gchar* escaped;
gchar* search;
g_return_val_if_fail (uri != NULL, NULL);
g_return_val_if_fail (keywords != NULL, NULL);
escaped = g_uri_escape_string (keywords, " :/", TRUE);
if (strstr (uri, "%s"))
search = g_strdup_printf (uri, escaped);
else
search = g_strconcat (uri, escaped, NULL);
g_free (escaped);
return search;
}
gchar*
sokoke_magic_uri (const gchar* uri,
KatzeArray* search_engines)
@ -279,13 +358,8 @@ sokoke_magic_uri (const gchar* uri,
if (parts[0] && parts[1])
if ((item = katze_array_find_token (search_engines, parts[0])))
{
gchar* uri_ = g_uri_escape_string (parts[1], " :/", TRUE);
search_uri = katze_item_get_uri (item);
if (strstr (search_uri, "%s"))
search = g_strdup_printf (search_uri, uri_);
else
search = g_strconcat (search_uri, uri_, NULL);
g_free (uri_);
search = sokoke_search_uri (search_uri, parts[1]);
}
g_strfreev (parts);
return search;
@ -353,8 +427,10 @@ sokoke_get_desktop (void)
/* Are we running in Xfce? */
gint result;
gchar *out = NULL;
gchar *err = NULL;
gboolean success = g_spawn_command_line_sync ("xprop -root _DT_SAVE_MODE",
&out, NULL, &result, NULL);
&out, &err, &result, NULL);
g_free (err);
if (success && ! result && out != NULL && strstr (out, "xfce4") != NULL)
desktop = SOKOKE_DESKTOP_XFCE;
else
@ -430,34 +506,6 @@ sokoke_xfce_header_new (const gchar* icon,
return NULL;
}
GtkWidget*
sokoke_superuser_warning_new (void)
{
/* Create a horizontal bar with a security warning
This returns NULL if the user is no superuser */
#if HAVE_UNISTD_H
if (G_UNLIKELY (!geteuid ())) /* effective superuser? */
{
GtkWidget* hbox;
GtkWidget* label;
hbox = gtk_event_box_new ();
gtk_widget_modify_bg (hbox, GTK_STATE_NORMAL,
&hbox->style->bg[GTK_STATE_SELECTED]);
/* i18n: A superuser, or system administrator, may not be 'root' */
label = gtk_label_new (_("Warning: You are using a superuser account!"));
gtk_misc_set_padding (GTK_MISC (label), 0, 2);
gtk_widget_modify_fg (GTK_WIDGET (label), GTK_STATE_NORMAL,
&GTK_WIDGET (label)->style->fg[GTK_STATE_SELECTED]);
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (hbox), GTK_WIDGET (label));
gtk_widget_show (hbox);
return hbox;
}
#endif
return NULL;
}
GtkWidget*
sokoke_hig_frame_new (const gchar* title)
{
@ -607,6 +655,25 @@ sokoke_key_file_get_boolean_default (GKeyFile* key_file,
return g_key_file_get_boolean (key_file, group, key, error);
}
gchar**
sokoke_key_file_get_string_list_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gsize* length,
gchar** default_value,
gsize* default_length,
GError* error)
{
gchar** value = g_key_file_get_string_list (key_file, group, key, length, NULL);
if (!value)
{
value = g_strdupv (default_value);
if (length)
*length = *default_length;
}
return value;
}
gboolean
sokoke_key_file_save_to_file (GKeyFile* key_file,
const gchar* filename,
@ -788,7 +855,7 @@ sokoke_register_stock_items (void)
{ STOCK_STYLES, N_("User_styles"), 0, 0, GTK_STOCK_SELECT_COLOR },
{ STOCK_TAB_NEW, N_("New _Tab"), 0, 0, GTK_STOCK_ADD },
{ STOCK_TRANSFERS, N_("_Transfers"), 0, 0, GTK_STOCK_SAVE },
{ STOCK_PLUGINS, N_("P_lugins"), 0, 0, GTK_STOCK_CONVERT },
{ STOCK_PLUGINS, N_("Netscape p_lugins"), 0, 0, GTK_STOCK_CONVERT },
{ STOCK_USER_TRASH, N_("_Closed Tabs and Windows"), 0, 0, "gtk-undo-ltr" },
{ STOCK_WINDOW_NEW, N_("New _Window"), 0, 0, GTK_STOCK_ADD },
};
@ -880,3 +947,119 @@ sokoke_remove_path (const gchar* path,
g_rmdir (path);
return TRUE;
}
static void
res_server_handler_cb (SoupServer* res_server,
SoupMessage* msg,
const gchar* path,
GHashTable* query,
SoupClientContext* client,
gpointer data)
{
if (g_str_has_prefix (path, "/res"))
{
gchar* filename = g_strconcat (DATADIR "/midori", path, NULL);
gchar* contents;
gsize length;
if (g_file_get_contents (filename, &contents, &length, NULL))
{
gchar* content_type = g_content_type_guess (filename, (guchar*)contents,
length, NULL);
gchar* mime_type = g_content_type_get_mime_type (content_type);
g_free (content_type);
soup_message_set_response (msg, mime_type, SOUP_MEMORY_TAKE,
contents, length);
g_free (mime_type);
soup_message_set_status (msg, 200);
}
else
soup_message_set_status (msg, 404);
g_free (filename);
}
else if (g_str_has_prefix (path, "/stock/"))
{
GtkIconTheme* icon_theme = gtk_icon_theme_get_default ();
const gchar* icon_name = &path[7] ? &path[7] : "";
gint icon_size = 22;
GdkPixbuf* icon;
gchar* contents;
gsize length;
if (g_ascii_isalpha (icon_name[0]))
icon_size = strstr (icon_name, "dialog") ? 48 : 22;
else if (g_ascii_isdigit (icon_name[0]))
{
guint i = 0;
while (icon_name[i])
if (icon_name[i++] == '/')
{
gchar* size = g_strndup (icon_name, i - 1);
icon_size = atoi (size);
g_free (size);
icon_name = &icon_name[i];
}
}
icon = gtk_icon_theme_load_icon (icon_theme, icon_name,
icon_size, 0, NULL);
if (!icon)
icon = gtk_icon_theme_load_icon (icon_theme, "gtk-missing-image",
icon_size, 0, NULL);
gdk_pixbuf_save_to_buffer (icon, &contents, &length, "png", NULL, NULL);
g_object_unref (icon);
soup_message_set_response (msg, "image/png", SOUP_MEMORY_TAKE,
contents, length);
soup_message_set_status (msg, 200);
}
else
{
soup_message_set_status (msg, 404);
}
}
SoupServer*
sokoke_get_res_server (void)
{
static SoupServer* res_server = NULL;
SoupAddress* addr = NULL;
if (G_UNLIKELY (!res_server))
{
addr = soup_address_new ("localhost", SOUP_ADDRESS_ANY_PORT);
soup_address_resolve_sync (addr, NULL);
res_server = soup_server_new ("interface", addr, NULL);
g_object_unref (addr);
soup_server_add_handler (res_server, "/",
res_server_handler_cb, NULL, NULL);
soup_server_run_async (res_server);
}
return res_server;
}
gchar*
sokoke_replace_variables (const gchar* template,
const gchar* variable_first, ...)
{
gchar* result = g_strdup (template);
const gchar* variable;
va_list args;
va_start (args, variable_first);
for (variable = variable_first; variable; variable = va_arg (args, const gchar*))
{
const gchar* value = va_arg (args, const gchar*);
GRegex* regex = g_regex_new (variable, 0, 0, NULL);
gchar* replaced = result;
result = g_regex_replace_literal (regex, replaced, -1, 0, value, 0, NULL);
g_free (replaced);
g_regex_unref (regex);
}
va_end (args);
return result;
}

View file

@ -1,5 +1,6 @@
/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -15,24 +16,34 @@
#include <katze/katze.h>
#include <gtk/gtk.h>
#include <libsoup/soup.h>
#include <JavaScriptCore/JavaScript.h>
gchar*
sokoke_js_script_eval (JSContextRef js_context,
const gchar* script,
gchar** exception);
sokoke_js_script_eval (JSContextRef js_context,
const gchar* script,
gchar** exception);
/* Many themes need this hack for small toolbars to work */
#define GTK_ICON_SIZE_SMALL_TOOLBAR GTK_ICON_SIZE_BUTTON
gboolean
sokoke_spawn_program (const gchar* command,
const gchar* argument);
sokoke_show_uri (GdkScreen* screen,
const gchar* uri,
guint32 timestamp,
GError** error);
gboolean
sokoke_spawn_program (const gchar* command,
const gchar* argument,
gboolean quote);
gchar* sokoke_search_uri (const gchar* uri,
const gchar* keywords);
gchar*
sokoke_magic_uri (const gchar* uri,
KatzeArray* search_engines);
sokoke_magic_uri (const gchar* uri,
KatzeArray* search_engines);
typedef enum {
SOKOKE_MENU_POSITION_CURSOR = 0,
@ -41,97 +52,110 @@ typedef enum {
} SokokeMenuPos;
void
sokoke_combo_box_add_strings (GtkComboBox* combobox,
const gchar* label_first,
...);
sokoke_combo_box_add_strings (GtkComboBox* combobox,
const gchar* label_first,
...);
void
sokoke_widget_set_visible (GtkWidget* widget,
gboolean visible);
sokoke_widget_set_visible (GtkWidget* widget,
gboolean visible);
void
sokoke_container_show_children (GtkContainer* container);
sokoke_container_show_children (GtkContainer* container);
void
sokoke_widget_popup (GtkWidget* widget,
GtkMenu* menu,
GdkEventButton* event,
SokokeMenuPos pos);
sokoke_widget_popup (GtkWidget* widget,
GtkMenu* menu,
GdkEventButton* event,
SokokeMenuPos pos);
GtkWidget*
sokoke_xfce_header_new (const gchar* icon,
const gchar* title);
sokoke_xfce_header_new (const gchar* icon,
const gchar* title);
GtkWidget*
sokoke_superuser_warning_new (void);
GtkWidget*
sokoke_hig_frame_new (const gchar* title);
sokoke_hig_frame_new (const gchar* title);
void
sokoke_widget_set_pango_font_style (GtkWidget* widget,
PangoStyle style);
sokoke_widget_set_pango_font_style (GtkWidget* widget,
PangoStyle style);
void
sokoke_entry_set_default_text (GtkEntry* entry,
const gchar* default_text);
sokoke_entry_set_default_text (GtkEntry* entry,
const gchar* default_text);
gchar*
sokoke_key_file_get_string_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
const gchar* default_value,
GError** error);
sokoke_key_file_get_string_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
const gchar* default_value,
GError** error);
gint
sokoke_key_file_get_integer_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
const gint default_value,
GError** error);
sokoke_key_file_get_integer_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
const gint default_value,
GError** error);
gdouble
sokoke_key_file_get_double_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gdouble default_value,
GError** error);
sokoke_key_file_get_double_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gdouble default_value,
GError** error);
gboolean
sokoke_key_file_get_boolean_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gboolean default_value,
GError** error);
sokoke_key_file_get_boolean_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gboolean default_value,
GError** error);
gchar**
sokoke_key_file_get_string_list_default (GKeyFile* key_file,
const gchar* group,
const gchar* key,
gsize* length,
gchar** default_value,
gsize* default_length,
GError* error);
gboolean
sokoke_key_file_save_to_file (GKeyFile* key_file,
const gchar* filename,
GError** error);
sokoke_key_file_save_to_file (GKeyFile* key_file,
const gchar* filename,
GError** error);
void
sokoke_widget_get_text_size (GtkWidget* widget,
const gchar* text,
gint* width,
gint* height);
sokoke_widget_get_text_size (GtkWidget* widget,
const gchar* text,
gint* width,
gint* height);
GtkWidget*
sokoke_action_create_popup_menu_item (GtkAction* action);
sokoke_action_create_popup_menu_item (GtkAction* action);
GtkWidget*
sokoke_image_menu_item_new_ellipsized (const gchar* label);
sokoke_image_menu_item_new_ellipsized (const gchar* label);
gint64
sokoke_time_t_to_julian (const time_t* timestamp);
sokoke_time_t_to_julian (const time_t* timestamp);
void
sokoke_register_stock_items (void);
sokoke_register_stock_items (void);
const gchar*
sokoke_set_config_dir (const gchar* new_config_dir);
sokoke_set_config_dir (const gchar* new_config_dir);
gboolean
sokoke_remove_path (const gchar* path,
gboolean ignore_errors);
sokoke_remove_path (const gchar* path,
gboolean ignore_errors);
SoupServer*
sokoke_get_res_server (void);
gchar*
sokoke_replace_variables (const gchar* template,
const gchar* variable_first, ...);
#endif /* !__SOKOKE_H__ */

View file

@ -527,11 +527,11 @@ midori_addons_finalize (GObject* object)
}
static gboolean
_metadata_from_file (const gchar* filename,
GSList** includes,
GSList** excludes,
gchar** name,
gchar** description)
js_metadata_from_file (const gchar* filename,
GSList** includes,
GSList** excludes,
gchar** name,
gchar** description)
{
GIOChannel* channel;
gboolean found_meta;
@ -601,6 +601,82 @@ _metadata_from_file (const gchar* filename,
return TRUE;
}
static gboolean
css_metadata_from_file (const gchar* filename,
GSList** includes,
GSList** excludes)
{
GIOChannel* channel;
gchar* line;
gchar* rest_of_line;
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))
return FALSE;
channel = g_io_channel_new_file (filename, "r", 0);
if (!channel)
return FALSE;
while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL)
== G_IO_STATUS_NORMAL)
{
if (g_str_has_prefix (line, "@namespace"))
; /* FIXME: Check "http://www.w3.org/1999/xhtml", skip otherwise */
else if (g_str_has_prefix (line, "@-moz-document"))
{ /* FIXME: We merely look for includes. We should honor blocks. */
if (includes)
{
gchar** parts;
guint i;
rest_of_line = g_strdup (line + strlen ("@-moz-document"));
rest_of_line = g_strstrip (rest_of_line);
parts = g_strsplit (rest_of_line, " ", 0);
i = 0;
while (parts[i])
{
if (g_str_has_prefix (parts[i], "url-prefix("))
{
gchar* value = g_strdup (parts[i] + strlen ("url-prefix("));
guint j;
if (value[0] != '\'' && value[0] != '"')
{
/* Wrong syntax, abort */
g_free (value);
g_strfreev (parts);
g_free (line);
g_io_channel_shutdown (channel, false, 0);
g_slist_free (*includes);
g_slist_free (*excludes);
*includes = NULL;
*excludes = NULL;
return FALSE;
}
j = 1;
while (value[j] != '\0')
{
if (value[j] == value[0])
break;
j++;
}
*includes = g_slist_prepend (*includes, g_strndup (value + 1, j - 1));
g_free (value);
}
/* FIXME: Recognize "domain" */
i++;
}
g_strfreev (parts);
}
}
g_free (line);
}
g_io_channel_shutdown (channel, false, 0);
g_io_channel_unref (channel);
return TRUE;
}
static gchar*
_convert_to_simple_regexp (const gchar* pattern)
{
@ -742,6 +818,7 @@ _js_style_from_file (JSContextRef js_context,
error = NULL;
if (g_file_get_contents (filename, &style, NULL, &error))
{
guint meta = 0;
n = strlen (style);
for (i = 0; i < n; i++)
{
@ -751,7 +828,34 @@ _js_style_from_file (JSContextRef js_context,
/* Change all single quotes to double quotes */
if (style[i] == '\'')
style[i] = '\"';
/* Turn metadata we inspected earlier into comments */
if (!meta && style[i] == '@')
{
style[i] = '/';
meta++;
}
else if (meta == 1 && (style[i] == '-' || style[i] == 'n'))
{
style[i] = '*';
meta++;
}
else if (meta == 2 && style[i] == '{')
{
style[i - 1] = '*';
style[i] = '/';
meta++;
}
else if (meta == 3 && style[i] == '{')
meta++;
else if (meta == 4 && style[i] == '}')
meta--;
else if (meta == 3 && style[i] == '}')
{
style[i] = ' ';
meta = 0;
}
}
style_script = g_strdup_printf (
"window.addEventListener ('DOMContentLoaded',"
"function () {"
@ -1031,8 +1135,8 @@ midori_addons_update_elements (MidoriAddons* addons)
if (addons->kind == MIDORI_ADDON_USER_SCRIPTS)
{
name = NULL;
if (!_metadata_from_file (fullname, &includes, &excludes,
&name, &description))
if (!js_metadata_from_file (fullname, &includes, &excludes,
&name, &description))
broken = TRUE;
if (name)
@ -1041,6 +1145,11 @@ midori_addons_update_elements (MidoriAddons* addons)
displayname = name;
}
}
else if (addons->kind == MIDORI_ADDON_USER_STYLES)
{
if (!css_metadata_from_file (fullname, &includes, &excludes))
broken = TRUE;
}
element = g_new (struct AddonElement, 1);
element->name = displayname;

View file

@ -116,10 +116,9 @@ midori_bookmarks_get_stock_id (MidoriViewable* viewable)
static void
midori_bookmarks_add_clicked_cb (GtkWidget* toolitem)
{
GtkWidget* browser = gtk_widget_get_toplevel (toolitem);
MidoriBrowser* browser = midori_browser_get_for_widget (toolitem);
/* FIXME: Take selected folder into account */
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser),
NULL, TRUE, FALSE);
midori_browser_edit_bookmark_dialog_new (browser, NULL, TRUE, FALSE);
}
static void
@ -140,9 +139,8 @@ midori_bookmarks_edit_clicked_cb (GtkWidget* toolitem,
is_separator = !KATZE_IS_ARRAY (item) && !katze_item_get_uri (item);
if (!is_separator)
{
GtkWidget* browser = gtk_widget_get_toplevel (toolitem);
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser),
item, FALSE, FALSE);
MidoriBrowser* browser = midori_browser_get_for_widget (toolitem);
midori_browser_edit_bookmark_dialog_new (browser, item, FALSE, FALSE);
}
g_object_unref (item);
@ -177,9 +175,9 @@ midori_bookmarks_delete_clicked_cb (GtkWidget* toolitem,
static void
midori_bookmarks_folder_clicked_cb (GtkWidget* toolitem)
{
GtkWidget* browser = gtk_widget_get_toplevel (toolitem);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (toolitem));
/* FIXME: Take selected folder into account */
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser),
midori_browser_edit_bookmark_dialog_new (browser,
NULL, TRUE, TRUE);
}
@ -614,10 +612,8 @@ midori_bookmarks_row_activated_cb (GtkTreeView* treeview,
uri = katze_item_get_uri (item);
if (uri && *uri)
{
GtkWidget* browser;
browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
midori_browser_set_current_uri (browser, uri);
}
g_object_unref (item);
@ -664,8 +660,8 @@ midori_bookmarks_open_activate_cb (GtkWidget* menuitem,
if (uri && *uri)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
midori_browser_set_current_uri (browser, uri);
}
}
@ -687,14 +683,14 @@ midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem,
{
if ((uri = katze_item_get_uri (child)) && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
n = midori_browser_add_item (MIDORI_BROWSER (browser), child);
browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
n = midori_browser_add_item (browser, child);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
i++;
@ -704,14 +700,14 @@ midori_bookmarks_open_in_tab_activate_cb (GtkWidget* menuitem,
{
if ((uri = katze_item_get_uri (item)) && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
n = midori_browser_add_item (MIDORI_BROWSER (browser), item);
browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
}
@ -729,7 +725,7 @@ midori_bookmarks_open_in_window_activate_cb (GtkWidget* menuitem,
if (uri && *uri)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
g_signal_emit_by_name (browser, "new-window", uri);
}
}
@ -746,8 +742,8 @@ midori_bookmarks_edit_activate_cb (GtkWidget* menuitem,
if (!is_separator)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (bookmarks));
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser), item, FALSE, FALSE);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (bookmarks));
midori_browser_edit_bookmark_dialog_new (browser, item, FALSE, FALSE);
}
}
@ -825,12 +821,12 @@ midori_bookmarks_button_release_event_cb (GtkWidget* widget,
if (uri && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
gint n;
browser = gtk_widget_get_toplevel (widget);
n = midori_browser_add_uri (MIDORI_BROWSER (browser), uri);
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
browser = midori_browser_get_for_widget (widget);
n = midori_browser_add_uri (browser, uri);
midori_browser_set_current_page (browser, n);
}
}
else

View file

@ -231,8 +231,8 @@ static void
midori_console_hierarchy_changed_cb (MidoriConsole* console,
GtkWidget* old_parent)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (console));
if (GTK_WIDGET_TOPLEVEL (browser))
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (console));
if (MIDORI_IS_BROWSER (browser))
g_signal_connect (browser, "add-tab",
G_CALLBACK (midori_console_browser_add_tab_cb), console);
}

View file

@ -199,7 +199,8 @@ midori_extensions_set_property (GObject* object,
case PROP_APP:
{
KatzeArray* array;
guint i, n;
MidoriExtension* extension;
guint i;
/* FIXME: Handle NULL and subsequent assignments */
extensions->app = g_value_get_object (value);
@ -207,10 +208,9 @@ midori_extensions_set_property (GObject* object,
g_signal_connect (array, "add-item",
G_CALLBACK (midori_extensions_add_item_cb), extensions);
if ((n = katze_array_get_length (array)))
for (i = 0; i < n; i++)
midori_extensions_add_item_cb (array,
katze_array_get_nth_item (array, i), extensions);
i = 0;
while ((extension = katze_array_get_nth_item (array, i++)))
midori_extensions_add_item_cb (array, extension, extensions);
}
break;
default:
@ -270,8 +270,11 @@ midori_extensions_treeview_render_text_cb (GtkTreeViewColumn* column,
g_free (name);
g_free (version);
g_free (desc);
g_object_set (renderer, "text", text, NULL);
g_object_set (renderer, "text", text,
"sensitive", midori_extension_is_active (extension),
NULL);
g_free (text);
g_object_unref (extension);
}
static void
@ -299,10 +302,12 @@ midori_extensions_treeview_row_activated_cb (GtkTreeView* treeview,
midori_extension_deactivate (extension);
else
g_signal_emit_by_name (extension, "activate", extensions->app);
gtk_widget_set_sensitive (GTK_WIDGET (button_enable),
!midori_extension_is_active (extension));
gtk_widget_set_sensitive (GTK_WIDGET (button_disable),
midori_extension_is_active (extension));
gtk_widget_set_sensitive (GTK_WIDGET (button_enable),
!midori_extension_is_active (extension));
gtk_widget_set_sensitive (GTK_WIDGET (button_disable),
midori_extension_is_active (extension));
/* FIXME: Update only the appropriate row */
gtk_widget_queue_draw (GTK_WIDGET (treeview));
}
}

View file

@ -117,10 +117,9 @@ midori_history_get_stock_id (MidoriViewable* viewable)
static void
midori_history_add_clicked_cb (GtkWidget* toolitem)
{
GtkWidget* browser = gtk_widget_get_toplevel (toolitem);
MidoriBrowser* browser = midori_browser_get_for_widget (toolitem);
/* FIXME: Take selected folder into account */
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser),
NULL, TRUE, FALSE);
midori_browser_edit_bookmark_dialog_new (browser, NULL, TRUE, FALSE);
}
static void
@ -152,11 +151,11 @@ static void
midori_history_clear_clicked_cb (GtkWidget* toolitem,
MidoriHistory* history)
{
GtkWidget* browser;
MidoriBrowser* browser;
GtkWidget* dialog;
gint result;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
browser = midori_browser_get_for_widget (GTK_WIDGET (history));
dialog = gtk_message_dialog_new (GTK_WINDOW (browser),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
@ -275,7 +274,8 @@ midori_history_clear_cb (KatzeArray* array,
static void
midori_history_disconnect_folder (MidoriHistory* history,
KatzeArray* array)
KatzeArray* array,
gboolean unref)
{
KatzeItem* item;
guint i;
@ -293,8 +293,9 @@ midori_history_disconnect_folder (MidoriHistory* history,
while ((item = katze_array_get_nth_item (array, i++)))
{
if (KATZE_IS_ARRAY (item))
midori_history_disconnect_folder (history, KATZE_ARRAY (item));
g_object_unref (item);
midori_history_disconnect_folder (history, KATZE_ARRAY (item), unref);
if (unref)
g_object_unref (item);
}
}
@ -309,6 +310,7 @@ midori_history_add_item_cb (KatzeArray* array,
g_return_if_fail (KATZE_IS_ARRAY (array));
g_return_if_fail (KATZE_IS_ITEM (added_item));
g_return_if_fail (MIDORI_IS_HISTORY (history));
if (KATZE_IS_ARRAY (added_item))
{
@ -392,7 +394,7 @@ midori_history_remove_item_cb (KatzeArray* array,
g_assert (KATZE_IS_ITEM (removed_item));
if (KATZE_IS_ARRAY (removed_item))
midori_history_disconnect_folder (history, KATZE_ARRAY (removed_item));
midori_history_disconnect_folder (history, KATZE_ARRAY (removed_item), TRUE);
model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
midori_history_remove_iter (model, NULL, removed_item);
@ -424,7 +426,7 @@ midori_history_clear_cb (KatzeArray* array,
midori_history_remove_item_cb (array, item, history);
}
midori_history_disconnect_folder (history, array);
midori_history_disconnect_folder (history, array, TRUE);
}
static void
@ -459,7 +461,6 @@ midori_history_insert_item (MidoriHistory* history,
age = day - pday;
gtk_tree_store_insert_with_values (treestore, &iter, parent,
0, 0, item, 1, age, -1);
g_object_unref (item);
piter = &iter;
}
i = 0;
@ -481,7 +482,7 @@ midori_history_set_app (MidoriHistory* history,
if (history->array)
{
midori_history_disconnect_folder (history, history->array);
midori_history_disconnect_folder (history, history->array, TRUE);
g_object_unref (history->array);
model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
gtk_tree_store_clear (GTK_TREE_STORE (model));
@ -499,7 +500,7 @@ midori_history_set_app (MidoriHistory* history,
model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
midori_history_insert_item (history, GTK_TREE_STORE (model),
NULL, KATZE_ITEM (g_object_ref (history->array)), day);
NULL, KATZE_ITEM (history->array), day);
}
}
@ -597,13 +598,13 @@ midori_history_treeview_render_text_cb (GtkTreeViewColumn* column,
}
else if (age > 6)
{
sdate = g_strdup_printf (_("A week ago"));
sdate = _("A week ago");
g_object_set (renderer, "text", sdate, NULL);
g_free (sdate);
}
else if (age > 1)
{
sdate = g_strdup_printf (_("%d days ago"), (gint)age);
sdate = g_strdup_printf (ngettext ("%d day ago",
"%d days ago", (gint)age), (gint)age);
g_object_set (renderer, "text", sdate, NULL);
g_free (sdate);
}
@ -641,10 +642,10 @@ midori_history_row_activated_cb (GtkTreeView* treeview,
uri = katze_item_get_uri (item);
if (uri && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri);
browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_set_current_uri (browser, uri);
}
g_object_unref (item);
@ -691,8 +692,8 @@ midori_history_open_activate_cb (GtkWidget* menuitem,
if (uri && *uri)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_set_current_uri (browser, uri);
}
}
@ -714,14 +715,14 @@ midori_history_open_in_tab_activate_cb (GtkWidget* menuitem,
{
if ((uri = katze_item_get_uri (child)) && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
n = midori_browser_add_item (MIDORI_BROWSER (browser), child);
browser = midori_browser_get_for_widget (GTK_WIDGET (history));
n = midori_browser_add_item (browser, child);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
i++;
@ -731,14 +732,14 @@ midori_history_open_in_tab_activate_cb (GtkWidget* menuitem,
{
if ((uri = katze_item_get_uri (item)) && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
n = midori_browser_add_item (MIDORI_BROWSER (browser), item);
browser = midori_browser_get_for_widget (GTK_WIDGET (history));
n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
midori_browser_set_current_page (browser, n);
g_object_unref (settings);
}
}
@ -756,7 +757,7 @@ midori_history_open_in_window_activate_cb (GtkWidget* menuitem,
if (uri && *uri)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (history));
g_signal_emit_by_name (browser, "new-window", uri);
}
}
@ -773,8 +774,8 @@ midori_history_bookmark_activate_cb (GtkWidget* menuitem,
if (uri && *uri)
{
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (history));
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser), item, TRUE, FALSE);
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_edit_bookmark_dialog_new (browser, item, TRUE, FALSE);
}
}
@ -852,12 +853,12 @@ midori_history_button_release_event_cb (GtkWidget* widget,
if (uri && *uri)
{
GtkWidget* browser;
MidoriBrowser* browser;
gint n;
browser = gtk_widget_get_toplevel (widget);
n = midori_browser_add_uri (MIDORI_BROWSER (browser), uri);
midori_browser_set_current_page (MIDORI_BROWSER (browser), n);
browser = midori_browser_get_for_widget (widget);
n = midori_browser_add_uri (browser, uri);
midori_browser_set_current_page (browser, n);
}
}
else
@ -967,6 +968,10 @@ midori_history_finalize (GObject* object)
if (history->app)
g_object_unref (history->app);
/* FIXME: We don't unref items (last argument is FALSE) because
our reference counting is incorrect. */
midori_history_disconnect_folder (history, history->array, FALSE);
g_object_unref (history->array);
}

View file

@ -84,7 +84,7 @@ midori_plugins_class_init (MidoriPluginsClass* class)
static const gchar*
midori_plugins_get_label (MidoriViewable* viewable)
{
return _("Plugins");
return _("Netscape plugins");
}
static const gchar*
@ -277,6 +277,10 @@ midori_plugins_init (MidoriPlugins* plugins)
const gchar* plugin_name;
const gchar* plugin_description;
/* Ignore files which don't have the correct suffix */
if (!g_str_has_suffix (filename, G_MODULE_SUFFIX))
continue;
fullname = g_build_filename (plugin_path, filename, NULL);
module = g_module_open (fullname, G_MODULE_BIND_LOCAL);
g_free (fullname);

View file

@ -100,8 +100,23 @@ midori_transfers_button_clear_clicked_cb (GtkToolItem* toolitem,
{
GtkTreeModel* model = gtk_tree_view_get_model (
GTK_TREE_VIEW (transfers->treeview));
/* FIXME: Clear only finished and cancelled downloads */
gtk_tree_store_clear (GTK_TREE_STORE (model));
GtkTreeIter iter;
gint n = 0;
while ((gtk_tree_model_iter_nth_child (model, &iter, NULL, n++)))
{
#if WEBKIT_CHECK_VERSION (1, 1, 3)
WebKitDownload* download;
WebKitDownloadStatus status;
gtk_tree_model_get (model, &iter, 1, &download, -1);
status = webkit_download_get_status (download);
if (status == WEBKIT_DOWNLOAD_STATUS_FINISHED
|| status == WEBKIT_DOWNLOAD_STATUS_CANCELLED)
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
g_object_unref (download);
#endif
}
}
static GtkWidget*
@ -173,8 +188,8 @@ midori_transfers_browser_add_download_cb (MidoriBrowser* browser,
treeview = GTK_TREE_VIEW (transfers->treeview);
model = gtk_tree_view_get_model (treeview);
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model),
NULL, NULL, G_MAXINT,
gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
NULL, G_MAXINT,
0, NULL, 1, download, -1);
g_signal_connect (download, "notify::progress",
G_CALLBACK (midori_transfers_download_notify_progress_cb), transfers);
@ -326,17 +341,10 @@ midori_transfers_treeview_row_activated_cb (GtkTreeView* treeview,
case WEBKIT_DOWNLOAD_STATUS_FINISHED:
{
const gchar* uri;
gboolean success;
uri = webkit_download_get_destination_uri (download);
success = gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (
sokoke_show_uri (gtk_widget_get_screen (GTK_WIDGET (
treeview)), uri, gtk_get_current_event_time (), NULL);
if (!success)
{
gchar* command = g_strconcat ("exo-open ", uri, NULL);
success = g_spawn_command_line_async (command, NULL);
g_free (command);
}
break;
}
case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
@ -354,10 +362,13 @@ midori_transfers_hierarchy_changed_cb (MidoriTransfers* transfers,
GtkWidget* old_parent)
{
#if WEBKIT_CHECK_VERSION (1, 1, 3)
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (transfers));
if (GTK_WIDGET_TOPLEVEL (browser))
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (transfers));
if (MIDORI_IS_BROWSER (browser))
g_signal_connect (browser, "add-download",
G_CALLBACK (midori_transfers_browser_add_download_cb), transfers);
if (old_parent)
g_signal_handlers_disconnect_by_func (old_parent,
midori_transfers_browser_add_download_cb, transfers);
#endif
}
@ -368,7 +379,7 @@ midori_transfers_init (MidoriTransfers* transfers)
GtkTreeViewColumn* column;
GtkCellRenderer* renderer_pixbuf;
GtkCellRenderer* renderer_text;
GtkTreeStore* treestore = gtk_tree_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_OBJECT);
GtkListStore* treestore = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_OBJECT);
transfers->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (treestore));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (transfers->treeview), FALSE);
column = gtk_tree_view_column_new ();

View file

@ -1,2 +1,2 @@
# set of available languages (in alphabetic order)
da de el en_GB es et fi fr gl he hu id it ja nl pl pt_PT ru sv tr uk zh_CN zh_TW
cs da de el en_GB es et fi fr gl he hu id it ja nl pl pt ro ru sk sv tr uk zh_CN zh_TW

View file

@ -24,8 +24,15 @@ katze/katze-utils.c
katze/katze-item.c
katze/katze-array.c
katze/katze-arrayaction.c
extensions/adblock.c
extensions/colorful-tabs.c
extensions/cookie-manager.c
extensions/cookie-manager/cookie-manager.c
extensions/cookie-manager/cookie-manager-page.c
extensions/feed-panel/feed-atom.c
extensions/feed-panel/feed-panel.c
extensions/feed-panel/feed-parse.c
extensions/feed-panel/feed-rss.c
extensions/feed-panel/main.c
extensions/mouse-gestures/main.c
extensions/page-holder.c
extensions/statusbar-features.c

2059
po/cs.po Normal file

File diff suppressed because it is too large Load diff

1112
po/da.po

File diff suppressed because it is too large Load diff

1200
po/de.po

File diff suppressed because it is too large Load diff

2408
po/el.po

File diff suppressed because it is too large Load diff

1680
po/es.po

File diff suppressed because it is too large Load diff

1402
po/fr.po

File diff suppressed because it is too large Load diff

1257
po/ja.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2135
po/pt.po Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2021
po/ro.po Normal file

File diff suppressed because it is too large Load diff

1264
po/ru.po

File diff suppressed because it is too large Load diff

1871
po/sk.po Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -38,7 +38,8 @@ browser_create (void)
GtkAction* action = actions->data;
if (g_strcmp0 (gtk_action_get_name (action), "WindowClose"))
if (g_strcmp0 (gtk_action_get_name (action), "EncodingCustom"))
gtk_action_activate (action);
if (g_strcmp0 (gtk_action_get_name (action), "AddSpeedDial"))
gtk_action_activate (action);
actions = g_list_next (actions);
}
g_list_free (actions);

View file

@ -15,6 +15,10 @@
#include "midori.h"
/* This is a private function */
GtkWidget*
midori_location_action_entry_for_proxy (GtkWidget* proxy);
typedef struct
{
const gchar* uri;
@ -41,7 +45,6 @@ completion_count (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
GtkTreeModel* model;
@ -49,8 +52,7 @@ completion_count (void)
action = g_object_new (MIDORI_TYPE_LOCATION_ACTION, NULL);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
alignment = gtk_bin_get_child (GTK_BIN (toolitem));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (location_entry));
@ -129,7 +131,6 @@ completion_fill (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
GtkTreeModel* model;
@ -138,8 +139,7 @@ completion_fill (void)
action = g_object_new (MIDORI_TYPE_LOCATION_ACTION, NULL);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
alignment = gtk_bin_get_child (GTK_BIN (toolitem));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
g_print ("...\n");
@ -217,7 +217,6 @@ completion_match (void)
{
MidoriLocationAction* action;
GtkWidget* toolitem;
GtkWidget* alignment;
GtkWidget* location_entry;
GtkWidget* entry;
GtkEntryCompletion* completion;
@ -230,8 +229,7 @@ completion_match (void)
midori_location_action_thaw (action);
toolitem = gtk_action_create_tool_item (GTK_ACTION (action));
alignment = gtk_bin_get_child (GTK_BIN (toolitem));
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
location_entry = midori_location_action_entry_for_proxy (toolitem);
entry = gtk_bin_get_child (GTK_BIN (location_entry));
completion = gtk_entry_get_completion (GTK_ENTRY (entry));
g_signal_connect (completion, "insert-prefix",

View file

@ -75,6 +75,21 @@ extension_mock_object (void)
return extension;
}
static void
katze_assert_cmpstrv (gchar** values1,
gsize length1,
gchar** values2,
gsize length2)
{
gsize i;
g_assert ((values1 && values2) || (!values1 && !values2));
/* g_assert_cmpint (length1, ==, length2); */
for (i = 0; i < length1; i++)
g_assert_cmpstr (values1[i], ==, values2[i]);
}
static void
extension_settings (void)
{
@ -83,6 +98,9 @@ extension_settings (void)
gboolean nihilist;
gint age;
const gchar* lastname;
gchar* pet_names[] = {"Tiger", "Bonzo", "Streuner", NULL};
gchar** names;
gsize names_n;
app = midori_app_new ();
extension = extension_mock_object ();
@ -126,6 +144,27 @@ extension_settings (void)
lastname = midori_extension_get_string (extension, "lastname");
g_assert_cmpstr (lastname, ==, "Theodor Fontane");
midori_extension_deactivate (extension);
extension = extension_mock_object ();
midori_extension_install_string_list (extension, "pets", pet_names, 3);
names = midori_extension_get_string_list (extension, "pets", &names_n);
katze_assert_cmpstrv (NULL, 0, names, names_n);
g_strfreev (names);
g_signal_emit_by_name (extension, "activate", app);
names = midori_extension_get_string_list (extension, "pets", &names_n);
katze_assert_cmpstrv (pet_names, 3, names, names_n);
g_strfreev (names);
/* names = g_strsplit ("Carla,Fluffy,Goro,Kitty", ",", 0);
midori_extension_set_string_list (extension, "pets", names, G_MAXSIZE);
g_strfreev (names);
names = midori_extension_get_string_list (extension, "pets", &names_n);
g_assert_cmpint (names_n, ==, 4); */
names = g_strsplit ("Carla,Fluffy,Goro,Kitty", ",", 0);
midori_extension_set_string_list (extension, "pets", names, 2);
g_strfreev (names);
names = midori_extension_get_string_list (extension, "pets", &names_n);
g_assert_cmpint (names_n, ==, 2);
midori_extension_deactivate (extension);
}
int

221
tests/history.c Normal file
View file

@ -0,0 +1,221 @@
/*
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.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "midori.h"
#include "midori-history.h"
#include "sokoke.h"
static void
history_panel_create (void)
{
MidoriApp* app;
MidoriHistory* history;
gpointer value;
app = g_object_new (MIDORI_TYPE_APP, NULL);
history = g_object_new (MIDORI_TYPE_HISTORY, NULL);
value = katze_object_get_object (history, "app");
g_assert (value == NULL);
gtk_widget_destroy (GTK_WIDGET (history));
history = g_object_new (MIDORI_TYPE_HISTORY, "app", app, NULL);
value = katze_object_get_object (history, "app");
g_assert (value == app);
gtk_widget_destroy (GTK_WIDGET (history));
history = g_object_new (MIDORI_TYPE_HISTORY, NULL);
g_object_set (history, "app", app, NULL);
value = katze_object_get_object (history, "app");
g_assert (value == app);
gtk_widget_destroy (GTK_WIDGET (history));
}
static KatzeItem*
bookmark_new (const gchar* uri,
const gchar* title)
{
return g_object_new (KATZE_TYPE_ITEM, "uri", uri, "name", title, NULL);
}
static KatzeArray*
folder_new (const gchar* title)
{
KatzeArray* folder;
folder = katze_array_new (KATZE_TYPE_ARRAY);
g_object_set (folder, "name", title, NULL);
return folder;
}
static void
history_panel_fill (void)
{
MidoriApp* app;
KatzeArray* array;
MidoriHistory* history;
GList* children;
GtkWidget* treeview;
GtkTreeModel* model;
GtkTreeIter iter;
KatzeItem* bookmark;
KatzeArray* folder;
guint n;
gpointer value;
app = g_object_new (MIDORI_TYPE_APP, NULL);
array = katze_array_new (KATZE_TYPE_ARRAY);
g_object_set (app, "history", array, NULL);
value = katze_object_get_object (app, "history");
g_assert (value == array);
history = g_object_new (MIDORI_TYPE_HISTORY, "app", app, NULL);
children = gtk_container_get_children (GTK_CONTAINER (history));
treeview = g_list_nth_data (children, 0);
g_list_free (children);
g_assert (GTK_IS_TREE_VIEW (treeview));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
g_assert (GTK_IS_TREE_MODEL (model));
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 0);
bookmark = bookmark_new ("http://www.example.com", "Example");
katze_array_add_item (array, bookmark);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 1);
katze_array_remove_item (array, bookmark);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 0);
bookmark = bookmark_new ("http://www.example.com", "Example");
katze_array_add_item (array, bookmark);
folder = folder_new ("Empty");
katze_array_add_item (array, folder);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 2);
katze_array_remove_item (array, folder);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 1);
folder = folder_new ("Empty");
katze_array_add_item (array, folder);
folder = folder_new ("Kurioses");
katze_array_add_item (array, folder);
bookmark = bookmark_new ("http://www.ende.de", "Das Ende");
katze_array_add_item (folder, bookmark);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 3);
folder = folder_new ("Miscellaneous");
katze_array_add_item (array, folder);
gtk_tree_model_iter_nth_child (model, &iter, NULL, 3);
n = gtk_tree_model_iter_n_children (model, &iter);
g_assert_cmpint (n, ==, 0);
bookmark = bookmark_new ("http://thesaurus.reference.com/", "Thesaurus");
katze_array_add_item (folder, bookmark);
n = gtk_tree_model_iter_n_children (model, &iter);
g_assert_cmpint (n, ==, 1);
bookmark = bookmark_new ("http://en.wikipedia.org/", "Wikipedia");
katze_array_add_item (folder, bookmark);
n = gtk_tree_model_iter_n_children (model, &iter);
g_assert_cmpint (n, ==, 2);
katze_array_remove_item (folder, bookmark);
n = gtk_tree_model_iter_n_children (model, &iter);
g_assert_cmpint (n, ==, 1);
katze_array_remove_item (array, folder);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 3);
katze_array_add_item (array, folder);
/* katze_array_clear (folder);
n = gtk_tree_model_iter_n_children (model, &iter);
g_assert_cmpint (n, ==, 0); */
katze_array_clear (array);
n = gtk_tree_model_iter_n_children (model, NULL);
g_assert_cmpint (n, ==, 0);
}
static void
notify_load_status_cb (GtkWidget* view,
GParamSpec* pspec,
guint* done)
{
MidoriLoadStatus status;
status = midori_view_get_load_status (MIDORI_VIEW (view));
if (*done == 2 && status == MIDORI_LOAD_COMMITTED)
*done = 1;
else if (*done == 1 && status == MIDORI_LOAD_FINISHED)
*done = 0;
}
static void
history_browser_add (void)
{
MidoriBrowser* browser;
MidoriWebSettings* settings;
KatzeArray* array;
GtkWidget* view;
guint done;
KatzeItem* date;
gsize i;
GtkActionGroup* action_group;
GtkAction* action;
browser = midori_browser_new ();
settings = midori_web_settings_new ();
array = katze_array_new (KATZE_TYPE_ARRAY);
g_object_set (browser, "settings", settings, "history", array, NULL);
view = midori_view_new (NULL);
midori_browser_add_tab (browser, view);
midori_view_set_uri (MIDORI_VIEW (view),
"data:text/html;charset=utf-8,<title>Test</title>Test");
g_signal_connect (view, "notify::load-status",
G_CALLBACK (notify_load_status_cb), &done);
done = 2;
while (done)
gtk_main_iteration ();
g_assert_cmpint (katze_array_get_length (array), ==, 1);
date = katze_array_get_nth_item (array, 0);
g_assert_cmpint (katze_array_get_length (KATZE_ARRAY (date)), ==, 1);
i = 0;
gtk_widget_show (view);
midori_browser_set_current_tab (browser, view);
g_assert (midori_browser_get_current_tab (browser) == view);
done = 2;
action_group = midori_browser_get_action_group (browser);
action = gtk_action_group_get_action (action_group, "Location");
midori_location_action_set_uri (MIDORI_LOCATION_ACTION (action),
"data:text/html;charset=utf-8,<title>Test</title>Test");
g_signal_emit_by_name (action, "submit-uri",
"data:text/html;charset=utf-8,<title>Test</title>Test", FALSE);
while (done)
gtk_main_iteration ();
g_assert_cmpint (katze_array_get_length (array), ==, 1);
date = katze_array_get_nth_item (array, 0);
g_assert_cmpint (katze_array_get_length (KATZE_ARRAY (date)), ==, 1);
i = 0;
}
int
main (int argc,
char** argv)
{
/* libSoup uses threads, so we need to initialize threads. */
if (!g_thread_supported ()) g_thread_init (NULL);
g_test_init (&argc, &argv, NULL);
gtk_init_check (&argc, &argv);
sokoke_register_stock_items ();
g_test_add_func ("/history/panel/create", history_panel_create);
g_test_add_func ("/history/panel/fill", history_panel_fill);
g_test_add_func ("/history/browser/add", history_browser_add);
return g_test_run ();
}

View file

@ -62,6 +62,7 @@ magic_uri_uri (void)
test_input ("http://example.com", "http://example.com");
test_input ("example.com", "http://example.com");
test_input ("example.com", "http://example.com");
test_input ("www.google..com", "http://www.google..com");
test_input ("/home/user/midori.html", "file:///home/user/midori.html");
a = g_get_current_dir ();
b = g_strconcat ("file://", a, G_DIR_SEPARATOR_S, "magic-uri.c", NULL);
@ -139,6 +140,7 @@ magic_uri_search (void)
test_input ("g conference \"April 2, 7:00 am\"", NULL);
test_input ("max@mustermann.de", NULL);
test_input ("g max@mustermann.de", NULL);
test_input ("g inurl:http://twotoasts.de bug", NULL);
}
static void

19
wscript
View file

@ -22,7 +22,7 @@ import UnitTest
major = 0
minor = 1
micro = 6
micro = 7
APPNAME = 'midori'
VERSION = str (major) + '.' + str (minor) + '.' + str (micro)
@ -212,7 +212,6 @@ def configure (conf):
print "User documentation: " + user_docs + " (docutils)"
print "API documentation: " + api_docs + " (gtk-doc)"
print
print "Optional run time dependencies:"
print "Single instance: " + unique + " (unique)"
if unique == 'yes' and conf.check_cfg (modversion='unique-1.0') == '1.0.4':
Utils.pprint ('RED', 'unique 1.0.4 found, this version is erroneous.')
@ -336,9 +335,13 @@ def build (bld):
' -o ' + blddir + '/data/logo-shade.png ' + \
srcdir + '/data/logo-shade.svg'
if not Utils.exec_command (command):
bld.install_files ('${DATADIR}/' + APPNAME, blddir + '/data/logo-shade.png')
bld.install_files ('${DATADIR}/' + APPNAME + '/res', blddir + '/data/logo-shade.png')
else:
Utils.pprint ('BLUE', "logo-shade could not be rasterized.")
bld.install_files ('${DATADIR}/' + APPNAME + '/res', 'data/error.html')
bld.install_files ('${DATADIR}/' + APPNAME + '/res', 'data/speeddial-head.html')
bld.install_files ('${DATADIR}/' + APPNAME + '/res', 'data/speeddial.json')
bld.install_files ('${DATADIR}/' + APPNAME + '/res', 'data/mootools.js')
if Options.commands['check']:
bld.add_subdirs ('tests')
@ -393,8 +396,12 @@ def shutdown ():
elif Options.options.run:
folder = os.path.dirname (Build.bld.env['waf_config_files'][0])
try:
ext = 'MIDORI_EXTENSION_PATH=' + folder + os.sep + 'extensions'
nls = 'NLSPATH=' + folder + os.sep + 'po'
relfolder = os.path.relpath (folder)
except:
pass
try:
ext = 'MIDORI_EXTENSION_PATH=' + relfolder + os.sep + 'extensions'
nls = 'NLSPATH=' + relfolder + os.sep + 'po'
lang = os.environ['LANG']
try:
for lang in os.listdir (folder + os.sep + 'po'):
@ -410,7 +417,7 @@ def shutdown ():
'LC_MESSAGES' + os.sep + APPNAME + '.mo')
except:
pass
command = folder + os.sep + APPNAME + os.sep + APPNAME
command = relfolder + os.sep + APPNAME + os.sep + APPNAME
print ext + ' ' + nls + ' ' + command
Utils.exec_command (ext + ' ' + nls + ' ' + command)
except: