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> Nick Schermer <nick@xfce.org>
Matthias Kruk <mkruk@matthiaskruk.de> Matthias Kruk <mkruk@matthiaskruk.de>
Johannes Reinhardt <jreinhardt@ist-dein-freund.de> Johannes Reinhardt <jreinhardt@ist-dein-freund.de>
Jean-François Guchens <zcx000@gmail.com>
Jérôme Geulfucci <jeromeg@xfce.org>
Graphics: Graphics:
extension: Nancy Runge <nancy@twotoasts.de> extension: Nancy Runge <nancy@twotoasts.de>
@ -23,6 +25,7 @@ Graphics:
logo-shade: Nancy Runge <nancy@twotoasts.de> logo-shade: Nancy Runge <nancy@twotoasts.de>
Translations: Translations:
cs: David Stancl <dstancl@dstancl.cz>
da: Per Kongstad <p_kongstad@op.pl> da: Per Kongstad <p_kongstad@op.pl>
de: Enrico Tröger <enrico.troeger@uvena.de> de: Enrico Tröger <enrico.troeger@uvena.de>
de: Christian Dywan <christian@twotoasts.de> de: Christian Dywan <christian@twotoasts.de>
@ -39,6 +42,7 @@ Translations:
fr: Adrien Nader <camaradetux@gmail.com> fr: Adrien Nader <camaradetux@gmail.com>
fr: Robert-André Mauchin <zebob.m@pengzone.org> fr: Robert-André Mauchin <zebob.m@pengzone.org>
fr: Pascal Gervais <pggervais@yahoo.ca> fr: Pascal Gervais <pggervais@yahoo.ca>
fr: Jérôme Geulfucci <jeromeg@xfce.org>
gl: Miguel Anxo Bouzada <mbouzada@gmail.com> gl: Miguel Anxo Bouzada <mbouzada@gmail.com>
he: Shlomi Israel <sijproject@gmail.com> he: Shlomi Israel <sijproject@gmail.com>
hu: SZERVÁC Attila <sas@321.hu> hu: SZERVÁC Attila <sas@321.hu>
@ -49,8 +53,11 @@ Translations:
pl: Przemysław Sitek <el.pescado@gazeta.pl> pl: Przemysław Sitek <el.pescado@gazeta.pl>
pl: Lukasz Romanowicz <romanowicz88@gmail.com> pl: Lukasz Romanowicz <romanowicz88@gmail.com>
pt_PT: Sérgio Marques <smarquespt@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: Troitskiy Nikita <niktr@mail.ru>
ru: Anton Shestakov <engored@ya.ru> ru: Anton Shestakov <engored@ya.ru>
sk: Robert Hartl <hartl.robert@gmail.com>
sv: Mikael Magnusson <mikachu@comhem.se> sv: Mikael Magnusson <mikachu@comhem.se>
tr: Mirat Can Bayrak <MiratCanBayrak@gmail.com> tr: Mirat Can Bayrak <MiratCanBayrak@gmail.com>
uk: Dmitry Nikitin <luckas_fb@mail.ru> 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. 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: v0.1.6:
+ Add Delete All to transferbar + Add Delete All to transferbar
+ Show search in context menu + 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 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. 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> #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 static void
colorful_tabs_view_notify_uri_cb (MidoriView* view, colorful_tabs_view_notify_uri_cb (MidoriView* view,
GParamSpec* pspec, 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 static void
colorful_tabs_browser_add_tab_cb (MidoriBrowser* browser, colorful_tabs_browser_add_tab_cb (MidoriBrowser* browser,
MidoriView* view, MidoriView* view,
@ -83,11 +93,12 @@ static void
colorful_tabs_deactivate_cb (MidoriExtension* extension, colorful_tabs_deactivate_cb (MidoriExtension* extension,
GtkWidget* bbox) GtkWidget* bbox)
{ {
gtk_widget_destroy (bbox);
g_signal_handlers_disconnect_by_func ( g_signal_handlers_disconnect_by_func (
extension, colorful_tabs_deactivate_cb, bbox); extension, colorful_tabs_deactivate_cb, bbox);
/* FIXME: Disconnect signals */ /* 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 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" #include "mouse-gestures.h"
#define MOUSE_GESTURES_VERSION "0.1" #define MOUSE_GESTURES_VERSION "0.1"
#define DEVIANCE 20 // the deviance to determine if a line is straight or not #define DEVIANCE 20
#define MINLENGTH 50 // the minimal length of a line to be treated as a gesture #define MINLENGTH 50
// #define __MOUSE_GESTURES_DEBUG__ // uncomment for debugging purposes /* #define __MOUSE_GESTURES_DEBUG__ */
MouseGesture *gesture; MouseGesture *gesture;
void mouse_gesture_clear(MouseGesture *g) void mouse_gesture_clear (MouseGesture *g)
{ {
g->start.x = 0; g->start.x = 0;
g->start.y = 0; g->start.y = 0;
g->middle.x = 0; g->middle.x = 0;
g->middle.y = 0; g->middle.y = 0;
g->end.x = 0; g->end.x = 0;
g->end.y = 0; g->end.y = 0;
g->last = MOUSE_BUTTON_UNSET; g->last = MOUSE_BUTTON_UNSET;
return;
} }
MouseGesture* mouse_gesture_new(void) MouseGesture* mouse_gesture_new (void)
{ {
MouseGesture *g = NULL; MouseGesture *g = g_new (MouseGesture, 1);
if((g = g_new(MouseGesture, 1)) == NULL) mouse_gesture_clear (g);
return(NULL);
mouse_gesture_clear(g); return 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 /* A button was pressed */
{ if (event->type == GDK_BUTTON_PRESS)
if(gesture->last == MOUSE_BUTTON_UNSET) // if the gesture was previously cleaned -> new gesture -> new start coordinates {
/* 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; /* Then we the final vertical move is between the bounds and
gesture->start.y = event->button.y; we moved right more than MINLENGTH pixels */
gesture->last = event->button.button; if ((gesture->middle.y - gesture->end.y < DEVIANCE) &&
} (gesture->middle.y - gesture->end.y > -DEVIANCE) &&
return(TRUE); (gesture->end.x > gesture->middle.x + MINLENGTH))
} {
else if(event->type == GDK_MOTION_NOTIFY) // the mouse was moved /* We moved down then right: close the tab */
{ midori_browser_activate_action (browser, "TabClose");
if(gesture->last != MOUSE_BUTTON_UNSET) }
/* 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; /* The end node was never updated, we only did a vertical move */
x = event->motion.x; if (gesture->end.y == 0 && gesture->end.x == 0)
y = event->motion.y; {
if((gesture->start.x - x < DEVIANCE && gesture->start.x - x > -DEVIANCE) || /* We moved up: stop */
(gesture->start.y - y < DEVIANCE && gesture->start.y - y > -DEVIANCE)) midori_browser_activate_action (browser, "Stop");
{ }
gesture->middle.x = x; }
gesture->middle.y = y; }
} /* The initial horizontal move is between the bounds */
else if((gesture->middle.x - x < DEVIANCE && gesture->middle.x - x > -DEVIANCE) || else if ((gesture->middle.y - gesture->start.y < DEVIANCE) &&
(gesture->middle.y - y < DEVIANCE && gesture->middle.y - y > -DEVIANCE)) (gesture->middle.y - gesture->start.y > -DEVIANCE))
{ {
gesture->end.x = x; /* We initially moved right more than MINLENGTH pixels */
gesture->end.y = y; if (gesture->middle.x > gesture->start.x + MINLENGTH)
} {
} /* The end node was never updated, we only did an horizontal move */
return(TRUE); if (gesture->end.x == 0 && gesture->end.y == 0)
} {
else if(event->type == GDK_BUTTON_RELEASE) /* We moved right: forward */
{ midori_browser_activate_action (browser, "Forward");
if(gesture->last == MOUSE_BUTTON_MIDDLE) // all mouse gestures will use the middle mouse button }
}
/* We initially moved left more than MINLENGTH pixels */
else if (gesture->middle.x + MINLENGTH < gesture->start.x)
{ {
// middle mouse button has been released /* The end node was never updated, we only did an horizontal move */
if(gesture->middle.x - gesture->start.x < DEVIANCE && gesture->middle.x - gesture->start.x > -DEVIANCE) if (gesture->end.x == 0 && gesture->end.y == 0)
{ {
// StartNode to MiddleNode is a straight vertical line /* We moved left: back */
if(gesture->middle.y > gesture->start.y + MINLENGTH) midori_browser_activate_action (browser, "Back");
{ }
// 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"); mouse_gesture_clear (gesture);
#ifdef __MOUSE_GESTURES_DEBUG__
g_print("TabClose gesture\n"); return TRUE;
#endif }
} else
else if(gesture->middle.y - gesture->end.y < DEVIANCE && gesture->middle.y - gesture->end.y > -DEVIANCE && gesture->end.x < gesture->middle.x - MINLENGTH) return FALSE;
{
// 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
} }
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 g_signal_connect (view, "event", G_CALLBACK (mouse_gestures_handle_events), browser);
return;
} }
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 g_signal_connect (browser, "add-tab", G_CALLBACK (mouse_gestures_tab_cb), NULL);
return;
} }
/* this is performed when the extension is deactivated. static void mouse_gestures_deactivate (MidoriExtension *extension, MidoriApp *app)
disconnect all signal handlers, so that mouse gestures are no longer handled */
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 gulong signal_id;
if(signal_id != 0) // if that id is valid KatzeArray *browsers;
g_signal_handler_disconnect(app, signal_id); // disconnect the signal guint i;
KatzeArray *browsers = katze_object_get_object(app, "browsers"); // get the array that contains all browsers gint j;
guint i; // our counter variable :) GtkWidget *notebook;
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
GtkWidget *notebook = katze_object_get_object(browser, "notebook"); // get a pointer on the notebook signal_id =
gint j; // another counter g_signal_handler_find (app, G_SIGNAL_MATCH_FUNC,
for(j = 0; j < gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); j++) // from the first to the last tab 0, 0, NULL,
{ mouse_gestures_browser_cb, NULL);
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(signal_id != 0) // if the id is valid g_signal_handler_disconnect (app, signal_id);
g_signal_handler_disconnect(page, signal_id); // disconnect the handler
} browsers = katze_object_get_object (app, "browsers");
}
g_free(gesture); // free the structure that contains the gesture information for (i = 0; i < katze_array_get_length (browsers); i++)
return; {
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 gesture = mouse_gesture_new ();
g_signal_connect(extension, "deactivate", G_CALLBACK(mouse_gestures_deactivate), app); // connect the deactivate callback to the extension
return; 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; MidoriExtension* extension;
extension = g_object_new(MIDORI_TYPE_EXTENSION,
"name", _("Mouse Gestures"), extension = g_object_new (MIDORI_TYPE_EXTENSION,
"description", _("Control Midori by moving the mouse"), "name", _("Mouse Gestures"),
"version", MOUSE_GESTURES_VERSION, "description", _("Control Midori by moving the mouse"),
"authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL); "version", MOUSE_GESTURES_VERSION,
g_signal_connect(extension, "activate", G_CALLBACK(mouse_gestures_activate), NULL); // connect the callback that's executed on activation "authors", "Matthias Kruk <mkruk@matthiaskruk.de>", NULL);
gesture = NULL; // initialise gesture pointer; initialising pointers is always a good choice
if((gesture = mouse_gesture_new()) == NULL) // allocate space for the gesture g_signal_connect (extension, "activate",
{ G_CALLBACK (mouse_gestures_activate), NULL);
// no space could be allocated
g_free(extension); // free the space used by this extension return extension;
return(NULL);
}
return(extension);
} }

View file

@ -27,7 +27,7 @@ page_holder_notebook_append_view (GtkWidget* notebook)
GtkWidget* label; GtkWidget* label;
view = midori_view_new (NULL); 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"); settings = katze_object_get_object (browser, "settings");
midori_view_set_settings (MIDORI_VIEW (view), settings); midori_view_set_settings (MIDORI_VIEW (view), settings);
g_object_unref (settings); g_object_unref (settings);
@ -49,7 +49,7 @@ page_holder_button_jump_to_clicked_cb (GtkWidget* button,
if (n < 0) if (n < 0)
n = page_holder_notebook_append_view (notebook); 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); uri = midori_browser_get_current_uri (browser);
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), n); view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), n);
midori_view_set_uri (MIDORI_VIEW (view), uri); 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); n = page_holder_notebook_append_view (notebook);
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), n); 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); uri = midori_browser_get_current_uri (browser);
midori_view_set_uri (MIDORI_VIEW (view), uri); 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_widget_show (image);
gtk_container_add (GTK_CONTAINER (button), image); gtk_container_add (GTK_CONTAINER (button), image);
#if GTK_CHECK_VERSION(2, 12, 0) #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 #endif
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
gtk_widget_show (button); gtk_widget_show (button);
@ -101,8 +101,8 @@ MidoriExtension*
extension_init (void) extension_init (void)
{ {
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION, MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", "Statusbar Features", "name", _("Statusbar Features"),
"description", "", "description", _("Easily toggle features on web pages on and off"),
"version", "0.1", "version", "0.1",
"authors", "Christian Dywan <christian@twotoasts.de>", "authors", "Christian Dywan <christian@twotoasts.de>",
NULL); NULL);

View file

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

View file

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

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 2008 Christian Dywan <christian@twotoasts.de> 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -13,6 +14,7 @@
#include "katze-net.h" #include "katze-net.h"
#include "katze-utils.h" #include "katze-utils.h"
#include "marshal.h"
#include <string.h> #include <string.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
@ -44,6 +46,7 @@ enum
{ {
POPULATE_POPUP, POPULATE_POPUP,
ACTIVATE_ITEM, ACTIVATE_ITEM,
ACTIVATE_ITEM_ALT,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -107,6 +110,30 @@ katze_array_action_class_init (KatzeArrayActionClass* class)
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
KATZE_TYPE_ITEM); 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 = G_OBJECT_CLASS (class);
gobject_class->finalize = katze_array_action_finalize; gobject_class->finalize = katze_array_action_finalize;
gobject_class->set_property = katze_array_action_set_property; gobject_class->set_property = katze_array_action_set_property;
@ -206,13 +233,35 @@ katze_array_action_activate (GtkAction* action)
} }
static void static void
katze_array_action_menu_item_activate_cb (GtkWidget* proxy, katze_array_action_menu_activate_cb (GtkWidget* proxy,
KatzeArrayAction* array_action) KatzeArrayAction* array_action)
{ {
KatzeItem* item = g_object_get_data (G_OBJECT (proxy), "KatzeItem"); KatzeItem* item = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
g_signal_emit (array_action, signals[ACTIVATE_ITEM], 0, item); 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 static void
katze_array_action_menu_item_select_cb (GtkWidget* proxy, katze_array_action_menu_item_select_cb (GtkWidget* proxy,
KatzeArrayAction* array_action); 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); G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
} }
else 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_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); gtk_widget_show (menuitem);
} }
if (!i) 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: * katze_array_action_create_tool_item_for:
* @array_action: a #KatzeArrayAction * @array_action: a #KatzeArrayAction
@ -453,6 +545,8 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
return gtk_separator_tool_item_new (); return gtk_separator_tool_item_new ();
toolitem = gtk_tool_button_new (NULL, NULL); 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)) if (KATZE_IS_ARRAY (item))
icon = gtk_widget_render_icon (GTK_WIDGET (toolitem), icon = gtk_widget_render_icon (GTK_WIDGET (toolitem),
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL); 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); string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget),
string ? string : ""); 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) if (pspec->flags & G_PARAM_WRITABLE)
g_signal_connect (widget, "selection-changed", g_signal_connect (widget, "selection-changed",
G_CALLBACK (proxy_file_file_set_cb), object); G_CALLBACK (proxy_file_file_set_cb), object);
#endif
} }
else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("folder")) 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); string = g_strdup (G_PARAM_SPEC_STRING (pspec)->default_value);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
string ? string : ""); 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) if (pspec->flags & G_PARAM_WRITABLE)
g_signal_connect (widget, "selection-changed", g_signal_connect (widget, "selection-changed",
G_CALLBACK (proxy_folder_file_set_cb), object); G_CALLBACK (proxy_folder_file_set_cb), object);
#endif
} }
else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("uri")) 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) 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -54,6 +55,22 @@ G_BEGIN_DECLS
lvalue = rvalue; \ 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* GtkWidget*
katze_property_proxy (gpointer object, katze_property_proxy (gpointer object,
const gchar* property, const gchar* property,

View file

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

View file

@ -28,12 +28,9 @@ gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry,
GdkPixbuf* pixbuf) GdkPixbuf* pixbuf)
{ {
/* Without this ugly hack pixbuf icons don't work */ /* 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_hide (GTK_WIDGET (entry)); gtk_widget_show (GTK_WIDGET (entry));
gtk_entry_set_icon_from_pixbuf (entry, position, pixbuf);
gtk_widget_show (GTK_WIDGET (entry));
}
} }
#else #else
@ -1266,6 +1263,10 @@ gtk_icon_entry_set_icon_from_stock (GtkIconEntry *entry,
{ {
GdkPixbuf *pixbuf; 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), pixbuf = gtk_widget_render_icon (GTK_WIDGET (entry),
stock_id, stock_id,
GTK_ICON_SIZE_MENU, GTK_ICON_SIZE_MENU,

View file

@ -39,6 +39,7 @@ G_BEGIN_DECLS
#define GTK_TYPE_ICON_ENTRY GTK_TYPE_ENTRY #define GTK_TYPE_ICON_ENTRY GTK_TYPE_ENTRY
#define gtk_icon_entry_new gtk_entry_new #define gtk_icon_entry_new gtk_entry_new
#define gtk_icon_entry_set_icon_from_stock gtk_entry_set_icon_from_stock #define gtk_icon_entry_set_icon_from_stock gtk_entry_set_icon_from_stock
void void
gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry, gtk_icon_entry_set_icon_from_pixbuf (GtkEntry* entry,
GtkEntryIconPosition position, GtkEntryIconPosition position,

View file

@ -35,6 +35,7 @@
#else #else
#define is_writable(_cfg_filename) 1 #define is_writable(_cfg_filename) 1
#endif #endif
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
@ -71,7 +72,8 @@ build_config_filename (const gchar* filename)
} }
static MidoriWebSettings* static MidoriWebSettings*
settings_new_from_file (const gchar* filename) settings_new_from_file (const gchar* filename,
gchar*** extensions)
{ {
MidoriWebSettings* settings = midori_web_settings_new (); MidoriWebSettings* settings = midori_web_settings_new ();
GKeyFile* key_file = g_key_file_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_warning (_("Invalid configuration value '%s'"), property);
} }
g_free (pspecs); g_free (pspecs);
*extensions = g_key_file_get_keys (key_file, "extensions", NULL, NULL);
g_key_file_free (key_file);
return settings; return settings;
} }
static gboolean static gboolean
settings_save_to_file (MidoriWebSettings* settings, settings_save_to_file (MidoriWebSettings* settings,
MidoriApp* app,
const gchar* filename, const gchar* filename,
GError** error) GError** error)
{ {
@ -172,6 +180,8 @@ settings_save_to_file (MidoriWebSettings* settings,
GType type; GType type;
const gchar* property; const gchar* property;
gboolean saved; gboolean saved;
KatzeArray* extensions = katze_object_get_object (app, "extensions");
MidoriExtension* extension;
key_file = g_key_file_new (); key_file = g_key_file_new ();
class = G_OBJECT_GET_CLASS (settings); class = G_OBJECT_GET_CLASS (settings);
@ -228,6 +238,14 @@ settings_save_to_file (MidoriWebSettings* settings,
g_warning (_("Invalid configuration value '%s'"), property); g_warning (_("Invalid configuration value '%s'"), property);
} }
g_free (pspecs); 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); saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file); g_key_file_free (key_file);
return saved; return saved;
@ -710,14 +728,15 @@ midori_app_quit_cb (MidoriApp* app)
static void static void
settings_notify_cb (MidoriWebSettings* settings, settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec) GParamSpec* pspec,
MidoriApp* app)
{ {
gchar* config_file; gchar* config_file;
GError* error; GError* error;
config_file = build_config_filename ("config"); config_file = build_config_filename ("config");
error = NULL; 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_warning (_("The configuration couldn't be saved. %s"), error->message);
g_error_free (error); g_error_free (error);
@ -1147,13 +1166,13 @@ static gboolean
midori_load_extensions (gpointer data) midori_load_extensions (gpointer data)
{ {
MidoriApp* app = MIDORI_APP (data); MidoriApp* app = MIDORI_APP (data);
gchar** active_extensions = g_object_get_data (G_OBJECT (app), "extensions");
KatzeArray* extensions; KatzeArray* extensions;
const gchar* filename;
MidoriExtension* extension; MidoriExtension* extension;
guint i;
/* Load extensions */ /* Load extensions */
extensions = katze_array_new (MIDORI_TYPE_EXTENSION); extensions = katze_array_new (MIDORI_TYPE_EXTENSION);
g_object_set (app, "extensions", extensions, NULL);
if (g_module_supported ()) if (g_module_supported ())
{ {
/* FIXME: Read extensions from system data dirs */ /* 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); extension_dir = g_dir_open (extension_path, 0, NULL);
if (extension_dir != NULL) if (extension_dir != NULL)
{ {
const gchar* filename;
while ((filename = g_dir_read_name (extension_dir))) while ((filename = g_dir_read_name (extension_dir)))
{ {
gchar* fullname; gchar* fullname;
@ -1186,6 +1207,8 @@ midori_load_extensions (gpointer data)
extension = extension_init (); extension = extension_init ();
/* FIXME: Validate the extension */ /* FIXME: Validate the extension */
/* Signal that we want the extension to load and save */ /* 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); midori_extension_get_config_dir (extension);
} }
else else
@ -1197,6 +1220,14 @@ midori_load_extensions (gpointer data)
g_warning ("%s", g_module_error ()); g_warning ("%s", g_module_error ());
} }
katze_array_add_item (extensions, extension); 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_object_unref (extension);
} }
g_dir_close (extension_dir); g_dir_close (extension_dir);
@ -1204,11 +1235,7 @@ midori_load_extensions (gpointer data)
g_free (extension_path); g_free (extension_path);
} }
g_object_set (app, "extensions", extensions, NULL); g_strfreev (active_extensions);
i = 0;
while ((extension = katze_array_get_nth_item (extensions, i++)))
g_signal_emit_by_name (extension, "activate", app);
return FALSE; return FALSE;
} }
@ -1318,6 +1345,33 @@ midori_run_script (const gchar* filename)
return 1; 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 int
main (int argc, main (int argc,
char** argv) char** argv)
@ -1325,6 +1379,7 @@ main (int argc,
gchar* webapp; gchar* webapp;
gchar* config; gchar* config;
gboolean run; gboolean run;
gchar* snapshot;
gboolean version; gboolean version;
gchar** uris; gchar** uris;
MidoriApp* app; MidoriApp* app;
@ -1338,6 +1393,10 @@ main (int argc,
N_("Use FOLDER as configuration folder"), N_("FOLDER") }, N_("Use FOLDER as configuration folder"), N_("FOLDER") },
{ "run", 'r', 0, G_OPTION_ARG_NONE, &run, { "run", 'r', 0, G_OPTION_ARG_NONE, &run,
N_("Run the specified filename as javascript"), NULL }, 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, { "version", 'V', 0, G_OPTION_ARG_NONE, &version,
N_("Display program version"), NULL }, N_("Display program version"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &uris, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &uris,
@ -1345,6 +1404,7 @@ main (int argc,
{ NULL } { NULL }
}; };
GString* error_messages; GString* error_messages;
gchar** extensions;
MidoriWebSettings* settings; MidoriWebSettings* settings;
gchar* config_file; gchar* config_file;
MidoriStartup load_on_startup; MidoriStartup load_on_startup;
@ -1379,6 +1439,7 @@ main (int argc,
webapp = NULL; webapp = NULL;
config = NULL; config = NULL;
run = FALSE; run = FALSE;
snapshot = NULL;
version = FALSE; version = FALSE;
uris = NULL; uris = NULL;
error = NULL; error = NULL;
@ -1412,6 +1473,39 @@ main (int argc,
return 0; 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 */ /* Web Application support */
if (webapp) if (webapp)
{ {
@ -1497,7 +1591,7 @@ main (int argc,
error_messages = g_string_new (NULL); error_messages = g_string_new (NULL);
config_file = build_config_filename ("config"); config_file = build_config_filename ("config");
error = NULL; 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")); katze_assign (config_file, build_config_filename ("accels"));
gtk_accel_map_load (config_file); gtk_accel_map_load (config_file);
katze_assign (config_file, build_config_filename ("search")); katze_assign (config_file, build_config_filename ("search"));
@ -1657,7 +1751,7 @@ main (int argc,
katze_assign (config_file, build_config_filename ("config")); katze_assign (config_file, build_config_filename ("config"));
if (is_writable (config_file)) if (is_writable (config_file))
g_signal_connect_after (settings, "notify", 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")); katze_assign (config_file, build_config_filename ("search"));
if (is_writable (config_file)) if (is_writable (config_file))
@ -1746,6 +1840,7 @@ main (int argc,
G_CALLBACK (midori_app_add_browser_cb), NULL); G_CALLBACK (midori_app_add_browser_cb), NULL);
g_idle_add (midori_load_cookie_jar, settings); g_idle_add (midori_load_cookie_jar, settings);
g_object_set_data (G_OBJECT (app), "extensions", extensions);
g_idle_add (midori_load_extensions, app); g_idle_add (midori_load_extensions, app);
katze_item_set_parent (KATZE_ITEM (_session), app); katze_item_set_parent (KATZE_ITEM (_session), app);
g_idle_add (midori_load_session, _session); g_idle_add (midori_load_session, _session);

View file

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

View file

@ -24,6 +24,20 @@
#include <unique/unique.h> #include <unique/unique.h>
#endif #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 struct _MidoriApp
{ {
GObject parent_instance; GObject parent_instance;
@ -41,6 +55,11 @@ struct _MidoriApp
KatzeArray* browsers; KatzeArray* browsers;
gpointer instance; gpointer instance;
/* libnotify handling */
gchar* program_notify_send;
GModule* libnotify_module;
LibNotifyFuncs libnotify_funcs;
}; };
struct _MidoriAppClass struct _MidoriAppClass
@ -52,6 +71,9 @@ struct _MidoriAppClass
(*add_browser) (MidoriApp* app, (*add_browser) (MidoriApp* app,
MidoriBrowser* browser); MidoriBrowser* browser);
void void
(*remove_browser) (MidoriApp* app,
MidoriBrowser* browser);
void
(*quit) (MidoriApp* app); (*quit) (MidoriApp* app);
}; };
@ -75,6 +97,7 @@ enum
enum { enum {
ADD_BROWSER, ADD_BROWSER,
REMOVE_BROWSER,
QUIT, QUIT,
LAST_SIGNAL LAST_SIGNAL
@ -85,6 +108,9 @@ static guint signals[LAST_SIGNAL];
static void static void
midori_app_finalize (GObject* object); midori_app_finalize (GObject* object);
static void
midori_app_init_libnotify (MidoriApp* app);
static void static void
midori_app_set_property (GObject* object, midori_app_set_property (GObject* object,
guint prop_id, guint prop_id,
@ -109,13 +135,18 @@ midori_browser_focus_in_event_cb (MidoriBrowser* browser,
static void static void
midori_browser_new_window_cb (MidoriBrowser* browser, midori_browser_new_window_cb (MidoriBrowser* browser,
const gchar* uri, MidoriBrowser* new_browser,
MidoriApp* app) MidoriApp* app)
{ {
MidoriBrowser* new_browser = midori_app_create_browser (app); g_object_set (new_browser,
midori_app_add_browser (app, 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)); gtk_widget_show (GTK_WIDGET (new_browser));
} }
@ -131,6 +162,7 @@ static gboolean
midori_browser_destroy_cb (MidoriBrowser* browser, midori_browser_destroy_cb (MidoriBrowser* browser,
MidoriApp* app) MidoriApp* app)
{ {
g_signal_emit (app, signals[REMOVE_BROWSER], 0, browser);
katze_array_remove_item (app->browsers, browser); katze_array_remove_item (app->browsers, browser);
if (!katze_array_is_empty (app->browsers)) if (!katze_array_is_empty (app->browsers))
return FALSE; return FALSE;
@ -160,6 +192,8 @@ _midori_app_add_browser (MidoriApp* app,
"signal::destroy", midori_browser_destroy_cb, app, "signal::destroy", midori_browser_destroy_cb, app,
"signal::quit", midori_browser_quit_cb, app, "signal::quit", midori_browser_quit_cb, app,
NULL); NULL);
g_signal_connect_swapped (browser, "send-notification",
G_CALLBACK (midori_app_send_notification), app);
katze_array_add_item (app->browsers, browser); katze_array_add_item (app->browsers, browser);
@ -191,6 +225,26 @@ midori_app_class_init (MidoriAppClass* class)
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
MIDORI_TYPE_BROWSER); 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 ( signals[QUIT] = g_signal_new (
"quit", "quit",
G_TYPE_FROM_CLASS (class), G_TYPE_FROM_CLASS (class),
@ -461,6 +515,8 @@ midori_app_init (MidoriApp* app)
app->browsers = katze_array_new (MIDORI_TYPE_BROWSER); app->browsers = katze_array_new (MIDORI_TYPE_BROWSER);
app->instance = NULL; app->instance = NULL;
midori_app_init_libnotify (app);
} }
static void static void
@ -481,6 +537,13 @@ midori_app_finalize (GObject* object)
katze_object_assign (app->instance, NULL); 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); G_OBJECT_CLASS (midori_app_parent_class)->finalize (object);
} }
@ -752,7 +815,7 @@ midori_app_add_browser (MidoriApp* app,
* *
* Return value: a new #MidoriBrowser * Return value: a new #MidoriBrowser
* *
* Since: 1.0.2 * Since: 0.1.2
**/ **/
MidoriBrowser* MidoriBrowser*
midori_app_create_browser (MidoriApp* app) midori_app_create_browser (MidoriApp* app)
@ -783,3 +846,86 @@ midori_app_quit (MidoriApp* app)
g_signal_emit (app, signals[QUIT], 0); 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 void
midori_app_quit (MidoriApp* app); midori_app_quit (MidoriApp* app);
void
midori_app_send_notification (MidoriApp* app,
const gchar* title,
const gchar* message);
G_END_DECLS G_END_DECLS
#endif /* __MIDORI_APP_H__ */ #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, midori_browser_remove_tab (MidoriBrowser* browser,
GtkWidget* widget); GtkWidget* widget);
void
midori_browser_foreach (MidoriBrowser* browser,
GtkCallback callback,
gpointer callback_data);
gint gint
midori_browser_add_item (MidoriBrowser* browser, midori_browser_add_item (MidoriBrowser* browser,
KatzeItem* item); KatzeItem* item);
@ -127,6 +132,9 @@ midori_browser_get_current_tab (MidoriBrowser* browser);
KatzeArray* KatzeArray*
midori_browser_get_proxy_array (MidoriBrowser* browser); midori_browser_get_proxy_array (MidoriBrowser* browser);
MidoriBrowser*
midori_browser_get_for_widget (GtkWidget* widget);
void void
midori_browser_quit (MidoriBrowser* browser); midori_browser_quit (MidoriBrowser* browser);

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 2008-2009 Christian Dywan <christian@twotoasts.de> 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -29,7 +30,7 @@ struct _MidoriExtensionPrivate
gchar* authors; gchar* authors;
MidoriApp* app; MidoriApp* app;
gboolean active; gint active;
gchar* config_dir; gchar* config_dir;
GList* lsettings; GList* lsettings;
GHashTable* settings; GHashTable* settings;
@ -60,20 +61,37 @@ typedef struct
gchar* value; gchar* value;
} MESettingString; } MESettingString;
typedef struct
{
gchar* name;
GType type;
gchar** default_value;
gchar** value;
gsize default_length;
gsize length;
} MESettingStringList;
void me_setting_free (gpointer setting) void me_setting_free (gpointer setting)
{ {
MESettingString* string_setting = (MESettingString*)setting; MESettingString* string_setting = (MESettingString*)setting;
MESettingStringList* strlist_setting = (MESettingStringList*)setting;
g_free (string_setting->name);
if (string_setting->type == G_TYPE_STRING) if (string_setting->type == G_TYPE_STRING)
{ {
g_free (string_setting->name);
g_free (string_setting->default_value); g_free (string_setting->default_value);
g_free (string_setting->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) \ #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 " \ g_critical ("%s: Settings have to be installed before " \
"the extension is activated.", G_STRFUNC); \ "the extension is activated.", G_STRFUNC); \
@ -276,6 +294,19 @@ midori_extension_activate_cb (MidoriExtension* extension,
else else
setting->value = g_strdup (setting->default_value); 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 else
g_assert_not_reached (); g_assert_not_reached ();
@ -283,7 +314,7 @@ midori_extension_activate_cb (MidoriExtension* extension,
} }
extension->priv->app = g_object_ref (app); extension->priv->app = g_object_ref (app);
extension->priv->active = TRUE; extension->priv->active = 1;
/* FIXME: Disconnect all signal handlers */ /* FIXME: Disconnect all signal handlers */
} }
@ -294,7 +325,7 @@ midori_extension_init (MidoriExtension* extension)
MIDORI_TYPE_EXTENSION, MidoriExtensionPrivate); MIDORI_TYPE_EXTENSION, MidoriExtensionPrivate);
extension->priv->app = NULL; extension->priv->app = NULL;
extension->priv->active = FALSE; extension->priv->active = 0;
extension->priv->config_dir = NULL; extension->priv->config_dir = NULL;
extension->priv->lsettings = NULL; extension->priv->lsettings = NULL;
extension->priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, 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); 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)); g_return_if_fail (midori_extension_is_active (extension));
extension->priv->active = 2;
g_signal_emit (extension, signals[DEACTIVATE], 0); g_signal_emit (extension, signals[DEACTIVATE], 0);
extension->priv->active = FALSE; extension->priv->active = 0;
katze_object_assign (extension->priv->app, NULL); katze_object_assign (extension->priv->app, NULL);
} }
@ -459,8 +514,7 @@ midori_extension_get_app (MidoriExtension* extension)
* @extension: a #MidoriExtension * @extension: a #MidoriExtension
* *
* Retrieves the path to a directory reserved for configuration * Retrieves the path to a directory reserved for configuration
* files specific to the extension. For that purpose the 'name' * files specific to the extension.
* of the extension is actually part of the path.
* *
* If settings are installed on the extension, they will be * If settings are installed on the extension, they will be
* loaded from and saved to a file "config" in this path. * loaded from and saved to a file "config" in this path.
@ -470,12 +524,16 @@ midori_extension_get_app (MidoriExtension* extension)
const gchar* const gchar*
midori_extension_get_config_dir (MidoriExtension* extension) midori_extension_get_config_dir (MidoriExtension* extension)
{ {
g_return_val_if_fail (midori_extension_is_prepared (extension), NULL); g_return_val_if_fail (midori_extension_is_prepared (extension), NULL);
if (!extension->priv->config_dir) 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 ( extension->priv->config_dir = g_build_filename (
sokoke_set_config_dir (NULL), "extensions", sokoke_set_config_dir (NULL), "extensions", filename, NULL);
extension->priv->name, NULL); }
return extension->priv->config_dir; 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 gboolean
midori_extension_is_active (MidoriExtension* extension); midori_extension_is_active (MidoriExtension* extension);
gboolean
midori_extension_is_deactivating (MidoriExtension* extension);
void void
midori_extension_deactivate (MidoriExtension* extension); midori_extension_deactivate (MidoriExtension* extension);
@ -105,6 +108,23 @@ midori_extension_set_string (MidoriExtension* extension,
const gchar* name, const gchar* name,
const gchar* value); 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 G_END_DECLS
#endif /* __MIDORI_EXTENSION_H__ */ #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> Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -189,12 +189,23 @@ midori_location_action_class_init (MidoriLocationActionClass* class)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 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 static void
midori_location_action_set_model (MidoriLocationAction* location_action, midori_location_action_set_model (MidoriLocationAction* location_action,
GtkTreeModel* model) GtkTreeModel* model)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry; GtkWidget* location_entry;
GtkWidget* entry; GtkWidget* entry;
@ -203,8 +214,7 @@ midori_location_action_set_model (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); location_entry = midori_location_action_entry_for_proxy (proxies->data);
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = gtk_bin_get_child (GTK_BIN (location_entry)); entry = gtk_bin_get_child (GTK_BIN (location_entry));
g_object_set (location_entry, "model", model, NULL); g_object_set (location_entry, "model", model, NULL);
@ -379,7 +389,6 @@ static void
midori_location_action_activate (GtkAction* action) midori_location_action_activate (GtkAction* action)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry; GtkWidget* entry;
proxies = gtk_action_get_proxies (action); proxies = gtk_action_get_proxies (action);
@ -387,8 +396,7 @@ midori_location_action_activate (GtkAction* action)
for (; proxies != NULL; proxies = g_slist_next (proxies)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (alignment));
/* Obviously only one widget can end up with the focus. /* Obviously only one widget can end up with the focus.
Yet we can't predict which one that is, can we? */ 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); 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* static GtkWidget*
midori_location_action_create_tool_item (GtkAction* action) midori_location_action_create_tool_item (GtkAction* action)
{ {
GtkWidget* toolitem; GtkWidget* toolitem;
GtkWidget* location_entry;
GtkWidget* alignment; GtkWidget* alignment;
GtkWidget* hbox;
GtkWidget* location_entry;
GtkWidget* go_button;
GtkWidget* go_icon;
toolitem = GTK_WIDGET (gtk_tool_item_new ()); toolitem = GTK_WIDGET (gtk_tool_item_new ());
gtk_tool_item_set_expand (GTK_TOOL_ITEM (toolitem), TRUE); 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); 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_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; return toolitem;
} }
@ -620,6 +657,10 @@ midori_location_action_set_item (MidoriLocationAction* location_action,
GdkPixbuf* icon; GdkPixbuf* icon;
GdkPixbuf* new_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, gtk_list_store_set (GTK_LIST_STORE (location_action->model), iter,
URI_COL, item->uri, TITLE_COL, item->title, -1); URI_COL, item->uri, TITLE_COL, item->title, -1);
@ -750,10 +791,7 @@ midori_location_entry_action_activated_cb (GtkEntryCompletion* completion,
gchar* search; gchar* search;
if (!item) if (!item)
return; return;
if (strstr (uri, "%s")) search = sokoke_search_uri (uri, keywords);
search = g_strdup_printf (uri, keywords);
else
search = g_strconcat (uri, " ", keywords, NULL);
midori_location_action_set_uri (location_action, search); midori_location_action_set_uri (location_action, search);
g_signal_emit (location_action, signals[SUBMIT_URI], 0, search, FALSE); g_signal_emit (location_action, signals[SUBMIT_URI], 0, search, FALSE);
g_free (search); g_free (search);
@ -858,7 +896,6 @@ static void
midori_location_action_connect_proxy (GtkAction* action, midori_location_action_connect_proxy (GtkAction* action,
GtkWidget* proxy) GtkWidget* proxy)
{ {
GtkWidget* alignment;
GtkWidget* entry; GtkWidget* entry;
MidoriLocationAction* location_action; MidoriLocationAction* location_action;
GtkCellRenderer* renderer; GtkCellRenderer* renderer;
@ -873,8 +910,7 @@ midori_location_action_connect_proxy (GtkAction* action,
if (GTK_IS_TOOL_ITEM (proxy)) if (GTK_IS_TOOL_ITEM (proxy))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxy)); entry = midori_location_action_entry_for_proxy (proxy);
entry = gtk_bin_get_child (GTK_BIN (alignment));
midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry), midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry),
MIDORI_LOCATION_ACTION (action)->progress); MIDORI_LOCATION_ACTION (action)->progress);
@ -942,7 +978,6 @@ midori_location_action_set_text (MidoriLocationAction* location_action,
const gchar* text) const gchar* text)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry; GtkWidget* location_entry;
GtkWidget* entry; GtkWidget* entry;
GtkTreeIter iter; GtkTreeIter iter;
@ -960,8 +995,7 @@ midori_location_action_set_text (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); location_entry = midori_location_action_entry_for_proxy (proxies->data);
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = gtk_bin_get_child (GTK_BIN (location_entry)); entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_entry_set_text (GTK_ENTRY (entry), text); gtk_entry_set_text (GTK_ENTRY (entry), text);
@ -990,7 +1024,6 @@ midori_location_action_set_icon (MidoriLocationAction* location_action,
GdkPixbuf* icon) GdkPixbuf* icon)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry; GtkWidget* location_entry;
GtkWidget* entry; GtkWidget* entry;
@ -1002,8 +1035,7 @@ midori_location_action_set_icon (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); location_entry = midori_location_action_entry_for_proxy (proxies->data);
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = gtk_bin_get_child (GTK_BIN (location_entry)); entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (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) const gchar* title)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry; GtkWidget* location_entry;
GtkWidget* entry; GtkWidget* entry;
MidoriLocationEntryItem item; MidoriLocationEntryItem item;
@ -1147,8 +1178,7 @@ midori_location_action_add_item (MidoriLocationAction* location_action,
if (GTK_IS_TOOL_ITEM (proxies->data) && if (GTK_IS_TOOL_ITEM (proxies->data) &&
!strcmp (location_action->uri, uri)) !strcmp (location_action->uri, uri))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); location_entry = midori_location_action_entry_for_proxy (proxies->data);
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = gtk_bin_get_child (GTK_BIN (location_entry)); entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (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) const gchar* uri)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* location_entry; GtkWidget* location_entry;
GtkWidget* entry; GtkWidget* entry;
MidoriLocationEntryItem item; MidoriLocationEntryItem item;
@ -1182,8 +1211,7 @@ midori_location_action_set_icon_for_uri (MidoriLocationAction* location_action,
if (GTK_IS_TOOL_ITEM (proxies->data) && if (GTK_IS_TOOL_ITEM (proxies->data) &&
!g_strcmp0 (location_action->uri, uri)) !g_strcmp0 (location_action->uri, uri))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); location_entry = midori_location_action_entry_for_proxy (proxies->data);
location_entry = gtk_bin_get_child (GTK_BIN (alignment));
entry = gtk_bin_get_child (GTK_BIN (location_entry)); entry = gtk_bin_get_child (GTK_BIN (location_entry));
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (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) KatzeArray* search_engines)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry; GtkWidget* entry;
GtkWidget* child; GtkWidget* child;
GtkEntryCompletion* completion; GtkEntryCompletion* completion;
@ -1241,8 +1268,7 @@ midori_location_action_set_search_engines (MidoriLocationAction* location_action
KatzeItem* item; KatzeItem* item;
guint i; guint i;
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (alignment));
child = gtk_bin_get_child (GTK_BIN (entry)); child = gtk_bin_get_child (GTK_BIN (entry));
completion = gtk_entry_get_completion (GTK_ENTRY (child)); completion = gtk_entry_get_completion (GTK_ENTRY (child));
@ -1270,7 +1296,6 @@ midori_location_action_set_progress (MidoriLocationAction* location_action,
gdouble progress) gdouble progress)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry; GtkWidget* entry;
g_return_if_fail (MIDORI_IS_LOCATION_ACTION (location_action)); 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)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (alignment));
midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry), midori_location_entry_set_progress (MIDORI_LOCATION_ENTRY (entry),
location_action->progress); location_action->progress);
@ -1295,7 +1319,6 @@ midori_location_action_set_secondary_icon (MidoriLocationAction* location_action
const gchar* stock_id) const gchar* stock_id)
{ {
GSList* proxies; GSList* proxies;
GtkWidget* alignment;
GtkWidget* entry; GtkWidget* entry;
GtkWidget* child; GtkWidget* child;
GtkStockItem stock_item; GtkStockItem stock_item;
@ -1310,16 +1333,11 @@ midori_location_action_set_secondary_icon (MidoriLocationAction* location_action
for (; proxies != NULL; proxies = g_slist_next (proxies)) for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data)) if (GTK_IS_TOOL_ITEM (proxies->data))
{ {
alignment = gtk_bin_get_child (GTK_BIN (proxies->data)); entry = midori_location_action_entry_for_proxy (proxies->data);
entry = gtk_bin_get_child (GTK_BIN (alignment));
child = gtk_bin_get_child (GTK_BIN (entry)); 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_set_icon_from_stock (GTK_ICON_ENTRY (child), GTK_ICON_ENTRY_SECONDARY, stock_id);
GTK_ICON_ENTRY_SECONDARY, stock_id);
else
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (child),
GTK_ICON_ENTRY_SECONDARY, NULL);
} }
} }

View file

@ -25,6 +25,7 @@ struct _MidoriPanel
GtkWidget* toolbar; GtkWidget* toolbar;
GtkToolItem* button_align; GtkToolItem* button_align;
GtkToolItem* button_detach;
GtkWidget* toolbar_label; GtkWidget* toolbar_label;
GtkWidget* frame; GtkWidget* frame;
GtkWidget* toolbook; 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* toolbar = g_object_get_data (G_OBJECT (scrolled), "panel-toolbar");
GtkWidget* menuitem = g_object_get_data (G_OBJECT (scrolled), "panel-menuitem"); GtkWidget* menuitem = g_object_get_data (G_OBJECT (scrolled), "panel-menuitem");
GtkToolItem* toolitem; GtkToolItem* toolitem;
gint n;
g_object_ref (toolbar); g_object_ref (toolbar);
gtk_container_remove (GTK_CONTAINER (vbox), toolbar); gtk_container_remove (GTK_CONTAINER (vbox), toolbar);
gtk_container_add (GTK_CONTAINER (panel->toolbook), toolbar); gtk_container_add (GTK_CONTAINER (panel->toolbook), toolbar);
g_object_unref (toolbar); g_object_unref (toolbar);
g_object_ref (scrolled); g_object_ref (scrolled);
gtk_container_remove (GTK_CONTAINER (vbox), 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); g_object_unref (scrolled);
toolitem = midori_panel_construct_tool_item (panel, toolitem = midori_panel_construct_tool_item (panel,
MIDORI_VIEWABLE (_midori_panel_child_for_scrolled (panel, scrolled))); 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); gtk_widget_show (menuitem);
g_object_set_data (G_OBJECT (menuitem), "toolitem", toolitem); 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; 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 static void
midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton, midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
MidoriPanel* panel) MidoriPanel* panel)
@ -221,7 +235,6 @@ midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
/* FIXME: What happens when the browser is destroyed? */ /* FIXME: What happens when the browser is destroyed? */
/* FIXME: What about multiple browsers? */ /* FIXME: What about multiple browsers? */
/* FIXME: Should we remember if the child was detached? */ /* 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); gint n = midori_panel_get_current_page (panel);
GtkToolItem* toolitem = gtk_toolbar_get_nth_item ( GtkToolItem* toolitem = gtk_toolbar_get_nth_item (
GTK_TOOLBAR (panel->toolbar), n); GTK_TOOLBAR (panel->toolbar), n);
@ -242,6 +255,9 @@ midori_panel_button_detach_clicked_cb (GtkWidget* toolbutton,
gtk_container_add (GTK_CONTAINER (window), vbox); gtk_container_add (GTK_CONTAINER (window), vbox);
if (menuitem) if (menuitem)
gtk_widget_hide (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)); gtk_container_remove (GTK_CONTAINER (panel->toolbar), GTK_WIDGET (toolitem));
g_object_ref (toolbar); g_object_ref (toolbar);
gtk_container_remove (GTK_CONTAINER (panel->toolbook), 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_container_remove (GTK_CONTAINER (panel->notebook), scrolled);
gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
g_object_unref (scrolled); 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_signal_connect (window, "delete-event",
G_CALLBACK (midori_panel_detached_window_delete_event_cb), panel); G_CALLBACK (midori_panel_detached_window_delete_event_cb), panel);
gtk_widget_show (window); gtk_widget_show (window);
@ -305,6 +327,8 @@ midori_panel_init (MidoriPanel* panel)
gtk_container_set_border_width (GTK_CONTAINER (toolitem), 6); gtk_container_set_border_width (GTK_CONTAINER (toolitem), 6);
gtk_toolbar_insert (GTK_TOOLBAR (labelbar), toolitem, -1); gtk_toolbar_insert (GTK_TOOLBAR (labelbar), toolitem, -1);
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_FULLSCREEN); 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), gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem),
_("Detach chosen panel from the window")); _("Detach chosen panel from the window"));
gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (toolitem), gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (toolitem),
@ -518,22 +542,14 @@ static void
midori_panel_viewable_destroy_cb (GtkWidget* viewable, midori_panel_viewable_destroy_cb (GtkWidget* viewable,
MidoriPanel* panel) 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) 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 ( g_signal_handlers_disconnect_by_func (
viewable, midori_panel_viewable_destroy_cb, panel); 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* static GtkToolItem*
midori_panel_construct_tool_item (MidoriPanel* panel, midori_panel_construct_tool_item (MidoriPanel* panel,
MidoriViewable* viewable) MidoriViewable* viewable)
@ -560,6 +576,10 @@ midori_panel_construct_tool_item (MidoriPanel* panel,
gtk_toolbar_insert (GTK_TOOLBAR (panel->toolbar), toolitem, -1); gtk_toolbar_insert (GTK_TOOLBAR (panel->toolbar), toolitem, -1);
g_signal_connect (viewable, "destroy", g_signal_connect (viewable, "destroy",
G_CALLBACK (midori_panel_widget_destroy_cb), toolitem); 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; return toolitem;
} }
@ -648,6 +668,7 @@ midori_panel_append_page (MidoriPanel* panel,
G_CALLBACK (midori_panel_widget_destroy_cb), menuitem); G_CALLBACK (midori_panel_widget_destroy_cb), menuitem);
} }
g_object_set_data (G_OBJECT (viewable), "parent", scrolled);
g_signal_connect (viewable, "destroy", g_signal_connect (viewable, "destroy",
G_CALLBACK (midori_panel_viewable_destroy_cb), panel); 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); FILLED_ADD (hbox, 1, 2, 1, 2);
button = katze_property_proxy (settings, "show-crash-dialog", NULL); 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")); FRAME_NEW (_("Transfers"));
TABLE_NEW (4, 2); TABLE_NEW (2, 2);
#if WEBKIT_CHECK_VERSION (1, 1, 3)
label = katze_property_label (settings, "download-folder"); label = katze_property_label (settings, "download-folder");
INDENTED_ADD (label, 0, 1, 0, 1); INDENTED_ADD (label, 0, 1, 0, 1);
button = katze_property_proxy (settings, "download-folder", "folder"); button = katze_property_proxy (settings, "download-folder", "folder");
FILLED_ADD (button, 1, 2, 0, 1); FILLED_ADD (button, 1, 2, 0, 1);
#endif label = katze_property_proxy (settings, "ask-for-destination-folder", NULL);
label = katze_property_label (settings, "download-manager"); gtk_widget_set_sensitive (label, FALSE);
INDENTED_ADD (label, 0, 1, 1, 2); INDENTED_ADD (label, 0, 1, 1, 2);
hbox = gtk_hbox_new (FALSE, 4); button = katze_property_proxy (settings, "notify-transfer-completed", NULL);
button = gtk_image_new (); /* FIXME: Disable the option if notifications presumably cannot be sent
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button), gtk_widget_set_sensitive (button, FALSE); */
GTK_ICON_SIZE_MENU, &icon_width, &icon_height); SPANNED_ADD (button, 1, 2, 1, 2);
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);
/* Page "Appearance" */ /* Page "Appearance" */
PAGE_NEW (GTK_STOCK_SELECT_FONT, _("Appearance")); PAGE_NEW (GTK_STOCK_SELECT_FONT, _("Appearance"));
FRAME_NEW (_("Font settings")); FRAME_NEW (_("Font settings"));
TABLE_NEW (6, 2); TABLE_NEW (7, 2);
label = gtk_label_new (_("Default Font Family")); label = gtk_label_new (_("Default Font Family"));
INDENTED_ADD (label, 0, 1, 0, 1); INDENTED_ADD (label, 0, 1, 0, 1);
hbox = gtk_hbox_new (FALSE, 4); hbox = gtk_hbox_new (FALSE, 4);
button = katze_property_proxy (settings, "default-font-family", "font"); 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); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
entry = katze_property_proxy (settings, "default-font-size", NULL); entry = katze_property_proxy (settings, "default-font-size", NULL);
gtk_widget_set_tooltip_text (entry, _("The default font size used to display text")); 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); gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 4);
FILLED_ADD (hbox, 1, 2, 0, 1); 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); 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); entry = katze_property_proxy (settings, "minimum-font-size", NULL);
gtk_widget_set_tooltip_text (entry, _("The minimum font size used to display text")); 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"); 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); 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"); label = katze_property_label (settings, "default-encoding");
gtk_label_set_label (GTK_LABEL (label), _("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); entry = katze_property_proxy (settings, "default-encoding", NULL);
gtk_widget_set_tooltip_text (entry, _("The character encoding to use by default")); gtk_widget_set_tooltip_text (entry, _("The character encoding to use by default"));
g_signal_connect (settings, "notify::preferred-encoding", g_signal_connect (settings, "notify::preferred-encoding",
G_CALLBACK (midori_preferences_notify_preferred_encoding_cb), entry); G_CALLBACK (midori_preferences_notify_preferred_encoding_cb), entry);
midori_preferences_notify_preferred_encoding_cb (settings, NULL, 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 "Behavior" */
PAGE_NEW (GTK_STOCK_SELECT_COLOR, _("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")); gtk_widget_set_tooltip_text (button, _("Enable embedded scripting languages"));
INDENTED_ADD (button, 0, 1, 2, 3); INDENTED_ADD (button, 0, 1, 2, 3);
button = katze_property_proxy (settings, "enable-plugins", NULL); button = katze_property_proxy (settings, "enable-plugins", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Enable plugins")); gtk_button_set_label (GTK_BUTTON (button), _("Enable Netscape plugins"));
gtk_widget_set_tooltip_text (button, _("Enable embedded plugin objects")); gtk_widget_set_tooltip_text (button, _("Enable embedded Netscape plugin objects"));
SPANNED_ADD (button, 1, 2, 2, 3); SPANNED_ADD (button, 1, 2, 2, 3);
button = katze_property_proxy (settings, "enforce-96-dpi", NULL); button = katze_property_proxy (settings, "enforce-96-dpi", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Enforce 96 dots per inch")); 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); SPANNED_ADD (button, 0, 1, 4, 5);
button = katze_property_proxy (settings, "find-while-typing", NULL); button = katze_property_proxy (settings, "find-while-typing", NULL);
SPANNED_ADD (button, 1, 2, 4, 5); 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 "Interface" */
PAGE_NEW (GTK_STOCK_CONVERT, _("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); button = katze_property_proxy (settings, "close-buttons-on-tabs", NULL);
WIDGET_ADD (button, 1, 2, 5, 6); 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 "Network" */
PAGE_NEW (GTK_STOCK_NETWORK, _("Network")); PAGE_NEW (GTK_STOCK_NETWORK, _("Network"));
FRAME_NEW (_("Network")); FRAME_NEW (_("Network"));

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 2007-2009 Christian Dywan <christian@twotoasts.de> 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -23,27 +24,27 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <webkit/webkit.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 */ /* This is unstable API, so we need to declare it */
gchar* gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view); 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 void
webkit_web_frame_print (WebKitWebFrame* web_frame); webkit_web_frame_print (WebKitWebFrame* web_frame);
#endif
GdkPixbuf* GdkPixbuf*
midori_search_action_get_icon (KatzeNet* net, midori_search_action_get_icon (KatzeNet* net,
KatzeItem* item, KatzeItem* item,
GtkWidget* widget); GtkWidget* widget);
static void
midori_view_construct_web_view (MidoriView* view);
struct _MidoriView struct _MidoriView
{ {
GtkScrolledWindow parent_instance; GtkScrolledWindow parent_instance;
@ -60,8 +61,9 @@ struct _MidoriView
gchar* selected_text; gchar* selected_text;
MidoriWebSettings* settings; MidoriWebSettings* settings;
GtkWidget* web_view; GtkWidget* web_view;
/* KatzeArray* news_feeds; */ KatzeArray* news_feeds;
gboolean speed_dial_in_new_tabs;
gchar* download_manager; gchar* download_manager;
gchar* news_aggregator; gchar* news_aggregator;
gboolean middle_click_opens_selection; gboolean middle_click_opens_selection;
@ -135,7 +137,7 @@ enum
PROP_LOAD_STATUS, PROP_LOAD_STATUS,
PROP_PROGRESS, PROP_PROGRESS,
PROP_ZOOM_LEVEL, PROP_ZOOM_LEVEL,
/* PROP_NEWS_FEEDS, */ PROP_NEWS_FEEDS,
PROP_STATUSBAR_TEXT, PROP_STATUSBAR_TEXT,
PROP_SETTINGS, PROP_SETTINGS,
PROP_NET PROP_NET
@ -153,6 +155,7 @@ enum {
SEARCH_TEXT, SEARCH_TEXT,
ADD_BOOKMARK, ADD_BOOKMARK,
SAVE_AS, SAVE_AS,
ADD_SPEED_DIAL,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -179,6 +182,16 @@ midori_view_settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec, GParamSpec* pspec,
MidoriView* view); 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 static void
midori_view_class_init (MidoriViewClass* class) midori_view_class_init (MidoriViewClass* class)
{ {
@ -354,6 +367,26 @@ midori_view_class_init (MidoriViewClass* class)
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
G_TYPE_STRING); 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 = G_OBJECT_CLASS (class);
gobject_class->finalize = midori_view_finalize; gobject_class->finalize = midori_view_finalize;
gobject_class->set_property = midori_view_set_property; gobject_class->set_property = midori_view_set_property;
@ -434,14 +467,21 @@ midori_view_class_init (MidoriViewClass* class)
1.0f, 1.0f,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 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, PROP_NEWS_FEEDS,
g_param_spec_object ( g_param_spec_object (
"news-feeds", "news-feeds",
"News Feeds", "News Feeds",
"The list of available news feeds", "The list of available news feeds",
KATZE_TYPE_ARRAY, KATZE_TYPE_ARRAY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); */ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_STATUSBAR_TEXT, PROP_STATUSBAR_TEXT,
@ -471,6 +511,52 @@ midori_view_class_init (MidoriViewClass* class)
flags)); 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* static GdkPixbuf*
midori_view_mime_icon (GtkIconTheme* icon_theme, midori_view_mime_icon (GtkIconTheme* icon_theme,
const gchar* format, const gchar* format,
@ -502,7 +588,10 @@ midori_view_update_icon (MidoriView* view,
{ {
icon_theme = gtk_icon_theme_get_for_screen (screen); icon_theme = gtk_icon_theme_get_for_screen (screen);
if ((parts = g_strsplit (view->mime_type, "/", 2))) 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 else
parts = NULL; parts = NULL;
@ -623,6 +712,55 @@ webkit_web_view_progress_changed_cb (WebKitWebView* web_view,
g_object_notify (G_OBJECT (view), "progress"); 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 static void
webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame, webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame,
gboolean success, gboolean success,
@ -653,6 +791,7 @@ webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame,
midori_view_update_load_status (view, MIDORI_LOAD_FINISHED); midori_view_update_load_status (view, MIDORI_LOAD_FINISHED);
} }
#endif
static void static void
webkit_web_view_load_finished_cb (WebKitWebView* web_view, 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) if (view->news_aggregator && *view->news_aggregator)
{ {
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame); 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, gchar* value = sokoke_js_script_eval (js_context,
"function feeds (l) { var f = new Array (); for (i in l) " "function feeds (l) { var f = new Array (); for (i in l) "
"{ var t = l[i].type; " "{ var t = l[i].type; "
"if (t && (t.indexOf ('rss') != -1 || t.indexOf ('atom') != -1)) " "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); "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", g_object_set_data (G_OBJECT (view), "news-feeds",
value && *value ? (void*)1 : (void*)0); value && *value ? (void*)1 : (void*)0);
g_free (value);
/* Ensure load-status is notified again, whether it changed or not */ /* Ensure load-status is notified again, whether it changed or not */
g_object_notify (G_OBJECT (view), "load-status"); 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)); 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 static void
webkit_web_view_title_changed_cb (WebKitWebView* web_view, webkit_web_view_title_changed_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame, WebKitWebFrame* web_frame,
@ -691,6 +870,7 @@ webkit_web_view_title_changed_cb (WebKitWebView* web_view,
{ {
g_object_set (view, "title", title, NULL); g_object_set (view, "title", title, NULL);
} }
#endif
static void static void
webkit_web_view_statusbar_text_changed_cb (WebKitWebView* web_view, 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: case 9:
midori_view_go_forward (view); midori_view_go_forward (view);
return TRUE; 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; return FALSE;
@ -829,10 +1020,15 @@ gtk_widget_key_press_event_cb (WebKitWebView* web_view,
if (character == (event->keyval | 0x01000000)) if (character == (event->keyval | 0x01000000))
return FALSE; 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)) && !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_signal_emit (view, signals[SEARCH_TEXT], 0, TRUE, text);
g_free (text); g_free (text);
@ -875,10 +1071,10 @@ midori_web_view_menu_new_tab_activate_cb (GtkWidget* widget,
} }
static void static void
midori_web_view_menu_new_window_activate_cb (GtkWidget* widget, midori_web_view_menu_action_add_speed_dial_cb (GtkWidget* widget,
MidoriView* view) 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 static void
@ -893,10 +1089,7 @@ midori_web_view_menu_search_web_activate_cb (GtkWidget* widget,
else else
g_object_get (view->settings, "location-entry-search", g_object_get (view->settings, "location-entry-search",
&search, NULL); &search, NULL);
if (strstr (search, "%s")) uri = sokoke_search_uri (search, view->selected_text);
uri = g_strdup_printf (search, view->selected_text);
else
uri = g_strconcat (search, view->selected_text, NULL);
g_free (search); g_free (search);
g_signal_emit (view, signals[NEW_TAB], 0, uri, g_signal_emit (view, signals[NEW_TAB], 0, uri,
@ -918,7 +1111,7 @@ static void
midori_web_view_menu_download_activate_cb (GtkWidget* widget, midori_web_view_menu_download_activate_cb (GtkWidget* widget,
MidoriView* view) MidoriView* view)
{ {
sokoke_spawn_program (view->download_manager, view->link_uri); sokoke_spawn_program (view->download_manager, view->link_uri, TRUE);
} }
static void static void
@ -993,9 +1186,6 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
/* hack to localize menu item */ /* hack to localize menu item */
label = gtk_bin_get_child (GTK_BIN (menuitem)); label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), _("Open Link in New _Window")); 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); menuitem = (GtkWidget*)g_list_nth_data (items, 3);
g_list_free (items); g_list_free (items);
#if WEBKIT_CHECK_VERSION (1, 1, 3) #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 */ /* hack to localize menu item */
if (GTK_IS_BIN (menuitem)) if (GTK_IS_BIN (menuitem))
{ {
label = gtk_bin_get_child (GTK_BIN (menuitem)); GtkStockItem stock_item;
gtk_label_set_label (GTK_LABEL (label), D_("gtk20", "_Refresh")); 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); g_list_free (items);
menuitem = gtk_image_menu_item_new_with_mnemonic (_("Undo Close Tab")); 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); G_CALLBACK (midori_web_view_menu_action_activate_cb), view);
/* FIXME: Make this sensitive only when there is a tab to undo */ /* FIXME: Make this sensitive only when there is a tab to undo */
gtk_widget_show (menuitem); gtk_widget_show (menuitem);
menuitem = gtk_separator_menu_item_new (); menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem); gtk_widget_show (menuitem);
menuitem = gtk_image_menu_item_new_from_stock (STOCK_BOOKMARK_ADD, NULL); menuitem = gtk_image_menu_item_new_from_stock (STOCK_BOOKMARK_ADD, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "BookmarkAdd"); g_object_set_data (G_OBJECT (menuitem), "action", "BookmarkAdd");
g_signal_connect (menuitem, "activate", g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_action_activate_cb), view); G_CALLBACK (midori_web_view_menu_action_activate_cb), view);
gtk_widget_show (menuitem); 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); menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "SaveAs"); 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. */ saving either. If that changes, we need to think of something. */
if (!midori_view_can_view_source (view)) if (!midori_view_can_view_source (view))
gtk_widget_set_sensitive (menuitem, FALSE); gtk_widget_set_sensitive (menuitem, FALSE);
menuitem = gtk_image_menu_item_new_with_mnemonic (_("View _Source")); menuitem = gtk_image_menu_item_new_with_mnemonic (_("View _Source"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "SourceView"); 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); gtk_widget_show (menuitem);
if (!midori_view_can_view_source (view)) if (!midori_view_can_view_source (view))
gtk_widget_set_sensitive (menuitem, FALSE); gtk_widget_set_sensitive (menuitem, FALSE);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL); menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "action", "Print"); 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, "net", view->net,
"settings", view->settings, "settings", view->settings,
NULL); 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_signal_connect (MIDORI_VIEW (new_view)->web_view, "web-view-ready",
G_CALLBACK (webkit_web_view_web_view_ready_cb), view); G_CALLBACK (webkit_web_view_web_view_ready_cb), view);
return MIDORI_VIEW (new_view)->web_view; return MIDORI_VIEW (new_view)->web_view;
@ -1322,14 +1531,20 @@ webkit_web_view_download_requested_cb (GtkWidget* web_view,
} }
#endif #endif
static void static gboolean
webkit_web_view_console_message_cb (GtkWidget* web_view, webkit_web_view_console_message_cb (GtkWidget* web_view,
const gchar* message, const gchar* message,
guint line, guint line,
const gchar* source_id, const gchar* source_id,
MidoriView* view) 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 static void
@ -1355,6 +1570,8 @@ midori_view_init (MidoriView* view)
view->statusbar_text = NULL; view->statusbar_text = NULL;
view->link_uri = NULL; view->link_uri = NULL;
view->selected_text = NULL; view->selected_text = NULL;
view->news_feeds = katze_array_new (KATZE_TYPE_ITEM);
view->item = NULL; view->item = NULL;
view->download_manager = NULL; view->download_manager = NULL;
@ -1382,6 +1599,7 @@ midori_view_finalize (GObject* object)
katze_assign (view->statusbar_text, NULL); katze_assign (view->statusbar_text, NULL);
katze_assign (view->link_uri, NULL); katze_assign (view->link_uri, NULL);
katze_assign (view->selected_text, NULL); katze_assign (view->selected_text, NULL);
katze_object_assign (view->news_feeds, NULL);
katze_object_assign (view->settings, NULL); katze_object_assign (view->settings, NULL);
katze_object_assign (view->item, NULL); katze_object_assign (view->item, NULL);
@ -1408,18 +1626,7 @@ midori_view_set_property (GObject* object,
{ {
case PROP_TITLE: case PROP_TITLE:
katze_assign (view->title, g_value_dup_string (value)); katze_assign (view->title, g_value_dup_string (value));
#define title midori_view_get_display_title (view) midori_view_update_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
break; break;
case PROP_ZOOM_LEVEL: case PROP_ZOOM_LEVEL:
midori_view_set_zoom_level (view, g_value_get_float (value)); 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: case PROP_ZOOM_LEVEL:
g_value_set_float (value, midori_view_get_zoom_level (view)); g_value_set_float (value, midori_view_get_zoom_level (view));
break; break;
case PROP_NEWS_FEEDS:
g_value_set_object (value, view->news_feeds);
break;
case PROP_STATUSBAR_TEXT: case PROP_STATUSBAR_TEXT:
g_value_set_string (value, view->statusbar_text); g_value_set_string (value, view->statusbar_text);
break; break;
@ -1509,6 +1719,7 @@ _midori_view_update_settings (MidoriView* view)
gboolean zoom_text_and_images; gboolean zoom_text_and_images;
g_object_get (view->settings, g_object_get (view->settings,
"speed-dial-in-new-tabs", &view->speed_dial_in_new_tabs,
"download-manager", &view->download_manager, "download-manager", &view->download_manager,
"news-aggregator", &view->news_aggregator, "news-aggregator", &view->news_aggregator,
"zoom-text-and-images", &zoom_text_and_images, "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_value_init (&value, pspec->value_type);
g_object_get_property (G_OBJECT (view->settings), name, &value); 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)); katze_assign (view->download_manager, g_value_dup_string (&value));
} }
@ -1710,6 +1925,8 @@ midori_view_construct_web_view (MidoriView* view)
WebKitWebFrame* web_frame; WebKitWebFrame* web_frame;
gpointer inspector; gpointer inspector;
g_return_if_fail (!view->web_view);
view->web_view = webkit_web_view_new (); view->web_view = webkit_web_view_new ();
/* Load something to avoid a bug where WebKit might not set a main frame */ /* 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, webkit_web_view_progress_changed_cb, view,
"signal::load-finished", "signal::load-finished",
webkit_web_view_load_finished_cb, view, 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", "signal::title-changed",
webkit_web_view_title_changed_cb, view, webkit_web_view_title_changed_cb, view,
#endif
"signal::status-bar-text-changed", "signal::status-bar-text-changed",
webkit_web_view_statusbar_text_changed_cb, view, webkit_web_view_statusbar_text_changed_cb, view,
"signal::hovering-over-link", "signal::hovering-over-link",
webkit_web_view_hovering_over_link_cb, view, webkit_web_view_hovering_over_link_cb, view,
"signal::button-press-event", "signal::button-press-event",
gtk_widget_button_press_event_cb, view, gtk_widget_button_press_event_cb, view,
"signal::key-press-event", "signal-after::key-press-event",
gtk_widget_key_press_event_cb, view, gtk_widget_key_press_event_cb, view,
"signal::scroll-event", "signal::scroll-event",
gtk_widget_scroll_event_cb, view, gtk_widget_scroll_event_cb, view,
@ -1751,12 +1975,18 @@ midori_view_construct_web_view (MidoriView* view)
"signal::download-requested", "signal::download-requested",
webkit_web_view_download_requested_cb, view, webkit_web_view_download_requested_cb, view,
#endif #endif
#if WEBKIT_CHECK_VERSION (1, 1, 6)
"signal::load-error",
webkit_web_view_load_error_cb, view,
#endif
NULL); NULL);
#if !WEBKIT_CHECK_VERSION (1, 1, 6)
g_object_connect (web_frame, g_object_connect (web_frame,
"signal::load-done", "signal::load-done",
webkit_web_frame_load_done_cb, view, webkit_web_frame_load_done_cb, view,
NULL); NULL);
#endif
if (view->settings) if (view->settings)
{ {
@ -1787,17 +2017,81 @@ midori_view_set_uri (MidoriView* view,
g_return_if_fail (MIDORI_IS_VIEW (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 (!uri) uri = "";
if (1) if (1)
{ {
if (!view->web_view) if (!view->web_view)
midori_view_construct_web_view (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 /* This is not prefectly elegant, but creating an
error page inline is the simplest solution. */ error page inline is the simplest solution. */
if (g_str_has_prefix (uri, "error:")) else if (g_str_has_prefix (uri, "error:"))
{ {
data = NULL; data = NULL;
#if !WEBKIT_CHECK_VERSION (1, 1, 3)
if (!strncmp (uri, "error:nodisplay ", 16)) if (!strncmp (uri, "error:nodisplay ", 16))
{ {
gchar* title; gchar* title;
@ -1814,7 +2108,8 @@ midori_view_set_uri (MidoriView* view,
title, title, view->uri, view->mime_type); title, title, view->uri, view->mime_type);
g_free (title); g_free (title);
} }
else if (!strncmp (uri, "error:nodocs ", 13)) #endif
if (!strncmp (uri, "error:nodocs ", 13))
{ {
gchar* title; gchar* title;
@ -1850,13 +2145,7 @@ midori_view_set_uri (MidoriView* view,
} }
else if (g_str_has_prefix (uri, "mailto:")) else if (g_str_has_prefix (uri, "mailto:"))
{ {
if (!gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL)) sokoke_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);
}
} }
else else
{ {
@ -1945,11 +2234,10 @@ midori_view_get_display_title (MidoriView* view)
{ {
g_return_val_if_fail (MIDORI_IS_VIEW (view), "about:blank"); 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) if (view->title && *view->title)
return view->title; return view->title;
if (midori_view_is_blank (view))
return _("Blank page");
return midori_view_get_display_uri (view); 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) if (angle == 0.0 || angle == 360.0)
{ {
gtk_widget_set_size_request (label, width * size, -1); 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 else
{ {
@ -2468,9 +2757,15 @@ midori_view_reload (MidoriView* view,
g_return_if_fail (MIDORI_IS_VIEW (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 /* Error pages are special, we want to try loading the destination
again, not the error page which isn't even a proper page */ again, not the error page which isn't even a proper page */
title = g_strdup_printf (_("Not found - %s"), view->uri); title = g_strdup_printf (_("Not found - %s"), view->uri);
#endif
if (view->title && strstr (title, view->title)) if (view->title && strstr (title, view->title))
webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), view->uri); webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), view->uri);
else if (from_cache) 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); webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view->web_view), script);
return TRUE; 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; MidoriStartup load_on_startup;
gchar* homepage; gchar* homepage;
gboolean show_crash_dialog; gboolean show_crash_dialog;
gboolean speed_dial_in_new_tabs;
gchar* download_folder; gchar* download_folder;
gboolean ask_for_destination_folder;
gboolean notify_transfer_completed;
gchar* download_manager; gchar* download_manager;
gchar* text_editor; gchar* text_editor;
gchar* news_aggregator; gchar* news_aggregator;
@ -81,6 +84,8 @@ struct _MidoriWebSettings
MidoriIdentity identify_as; MidoriIdentity identify_as;
gchar* ident_string; gchar* ident_string;
gint cache_size; gint cache_size;
gint clear_private_data;
}; };
struct _MidoriWebSettingsClass struct _MidoriWebSettingsClass
@ -119,7 +124,10 @@ enum
PROP_LOAD_ON_STARTUP, PROP_LOAD_ON_STARTUP,
PROP_HOMEPAGE, PROP_HOMEPAGE,
PROP_SHOW_CRASH_DIALOG, PROP_SHOW_CRASH_DIALOG,
PROP_SPEED_DIAL_IN_NEW_TABS,
PROP_DOWNLOAD_FOLDER, PROP_DOWNLOAD_FOLDER,
PROP_ASK_FOR_DESTINATION_FOLDER,
PROP_NOTIFY_TRANSFER_COMPLETED,
PROP_DOWNLOAD_MANAGER, PROP_DOWNLOAD_MANAGER,
PROP_TEXT_EDITOR, PROP_TEXT_EDITOR,
PROP_NEWS_AGGREGATOR, PROP_NEWS_AGGREGATOR,
@ -135,8 +143,6 @@ enum
PROP_OPEN_TABS_NEXT_TO_CURRENT, PROP_OPEN_TABS_NEXT_TO_CURRENT,
PROP_OPEN_POPUPS_IN_TABS, PROP_OPEN_POPUPS_IN_TABS,
PROP_ENFORCE_96_DPI,
PROP_ENABLE_DEVELOPER_EXTRAS,
PROP_ZOOM_TEXT_AND_IMAGES, PROP_ZOOM_TEXT_AND_IMAGES,
PROP_FIND_WHILE_TYPING, PROP_FIND_WHILE_TYPING,
PROP_ACCEPT_COOKIES, PROP_ACCEPT_COOKIES,
@ -152,7 +158,9 @@ enum
PROP_AUTO_DETECT_PROXY, PROP_AUTO_DETECT_PROXY,
PROP_IDENTIFY_AS, PROP_IDENTIFY_AS,
PROP_IDENT_STRING, PROP_IDENT_STRING,
PROP_CACHE_SIZE PROP_CACHE_SIZE,
PROP_CLEAR_PRIVATE_DATA
}; };
GType GType
@ -541,7 +549,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("Load on Startup"), _("Load on Startup"),
_("What to load on startup"), _("What to load on startup"),
MIDORI_TYPE_STARTUP, MIDORI_TYPE_STARTUP,
MIDORI_STARTUP_HOMEPAGE, MIDORI_STARTUP_LAST_OPEN_PAGES,
flags)); flags));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
@ -569,6 +577,27 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
TRUE, TRUE,
flags)); 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, g_object_class_install_property (gobject_class,
PROP_DOWNLOAD_FOLDER, PROP_DOWNLOAD_FOLDER,
g_param_spec_string ( g_param_spec_string (
@ -582,6 +611,46 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
#endif #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, g_object_class_install_property (gobject_class,
PROP_DOWNLOAD_MANAGER, PROP_DOWNLOAD_MANAGER,
g_param_spec_string ( g_param_spec_string (
@ -701,7 +770,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
g_param_spec_boolean ( g_param_spec_boolean (
"open-tabs-next-to-current", "open-tabs-next-to-current",
_("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, TRUE,
flags)); flags));
@ -882,6 +951,23 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("The allowed size of the cache"), _("The allowed size of the cache"),
0, G_MAXINT, 100, 0, G_MAXINT, 100,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); 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 static void
@ -889,7 +975,7 @@ notify_default_encoding_cb (GObject* object,
GParamSpec* pspec) GParamSpec* pspec)
{ {
MidoriWebSettings* web_settings; MidoriWebSettings* web_settings;
const gchar* string; gchar* string;
const gchar* encoding; const gchar* encoding;
web_settings = MIDORI_WEB_SETTINGS (object); web_settings = MIDORI_WEB_SETTINGS (object);
@ -908,12 +994,14 @@ notify_default_encoding_cb (GObject* object,
web_settings->preferred_encoding = MIDORI_ENCODING_WESTERN; web_settings->preferred_encoding = MIDORI_ENCODING_WESTERN;
else else
web_settings->preferred_encoding = MIDORI_ENCODING_CUSTOM; web_settings->preferred_encoding = MIDORI_ENCODING_CUSTOM;
g_free (string);
g_object_notify (object, "preferred-encoding"); g_object_notify (object, "preferred-encoding");
} }
static void static void
midori_web_settings_init (MidoriWebSettings* web_settings) 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->download_folder = g_strdup (midori_get_download_dir ());
web_settings->http_proxy = NULL; web_settings->http_proxy = NULL;
web_settings->open_popups_in_tabs = TRUE; web_settings->open_popups_in_tabs = TRUE;
@ -928,6 +1016,20 @@ midori_web_settings_init (MidoriWebSettings* web_settings)
static void static void
midori_web_settings_finalize (GObject* object) 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); 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: case PROP_SHOW_CRASH_DIALOG:
web_settings->show_crash_dialog = g_value_get_boolean (value); web_settings->show_crash_dialog = g_value_get_boolean (value);
break; 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: case PROP_DOWNLOAD_FOLDER:
katze_assign (web_settings->download_folder, g_value_dup_string (value)); katze_assign (web_settings->download_folder, g_value_dup_string (value));
break; 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: case PROP_DOWNLOAD_MANAGER:
katze_assign (web_settings->download_manager, g_value_dup_string (value)); katze_assign (web_settings->download_manager, g_value_dup_string (value));
break; break;
@ -1190,6 +1301,9 @@ midori_web_settings_set_property (GObject* object,
case PROP_CACHE_SIZE: case PROP_CACHE_SIZE:
web_settings->cache_size = g_value_get_int (value); web_settings->cache_size = g_value_get_int (value);
break; break;
case PROP_CLEAR_PRIVATE_DATA:
web_settings->clear_private_data = g_value_get_int (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -1275,9 +1389,18 @@ midori_web_settings_get_property (GObject* object,
case PROP_SHOW_CRASH_DIALOG: case PROP_SHOW_CRASH_DIALOG:
g_value_set_boolean (value, web_settings->show_crash_dialog); g_value_set_boolean (value, web_settings->show_crash_dialog);
break; 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: case PROP_DOWNLOAD_FOLDER:
g_value_set_string (value, web_settings->download_folder); g_value_set_string (value, web_settings->download_folder);
break; 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: case PROP_DOWNLOAD_MANAGER:
g_value_set_string (value, web_settings->download_manager); g_value_set_string (value, web_settings->download_manager);
break; break;
@ -1319,12 +1442,6 @@ midori_web_settings_get_property (GObject* object,
g_value_set_boolean (value, web_settings->open_popups_in_tabs); g_value_set_boolean (value, web_settings->open_popups_in_tabs);
break; 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: case PROP_ZOOM_TEXT_AND_IMAGES:
g_value_set_boolean (value, web_settings->zoom_text_and_images); g_value_set_boolean (value, web_settings->zoom_text_and_images);
break; break;
@ -1374,6 +1491,9 @@ midori_web_settings_get_property (GObject* object,
case PROP_CACHE_SIZE: case PROP_CACHE_SIZE:
g_value_set_int (value, web_settings->cache_size); g_value_set_int (value, web_settings->cache_size);
break; break;
case PROP_CLEAR_PRIVATE_DATA:
g_value_set_int (value, web_settings->clear_private_data);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 2007-2009 Christian Dywan <christian@twotoasts.de> 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -12,6 +13,8 @@
#include "sokoke.h" #include "sokoke.h"
#include "midori-stock.h" #include "midori-stock.h"
#include "compat.h"
#if HAVE_CONFIG_H #if HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif #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 gboolean
sokoke_spawn_program (const gchar* command, sokoke_spawn_program (const gchar* command,
const gchar* argument) const gchar* argument,
gboolean quote)
{ {
gchar* argument_escaped; gchar* argument_escaped;
gchar* command_ready; 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 (command != NULL, FALSE);
g_return_val_if_fail (argument != 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")) if (strstr (command, "%s"))
command_ready = g_strdup_printf (command, argument_escaped); command_ready = g_strdup_printf (command, argument_escaped);
else else
@ -175,6 +222,8 @@ sokoke_idn_to_punycode (gchar* uri)
g_utf8_strncpy (buffer, hostname, offset); g_utf8_strncpy (buffer, hostname, offset);
hostname = buffer; hostname = buffer;
} }
else
hostname = g_strdup (hostname);
} }
else else
hostname = g_strdup (uri); hostname = g_strdup (uri);
@ -213,6 +262,36 @@ sokoke_idn_to_punycode (gchar* uri)
#endif #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* gchar*
sokoke_magic_uri (const gchar* uri, sokoke_magic_uri (const gchar* uri,
KatzeArray* search_engines) KatzeArray* search_engines)
@ -279,13 +358,8 @@ sokoke_magic_uri (const gchar* uri,
if (parts[0] && parts[1]) if (parts[0] && parts[1])
if ((item = katze_array_find_token (search_engines, parts[0]))) 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); search_uri = katze_item_get_uri (item);
if (strstr (search_uri, "%s")) search = sokoke_search_uri (search_uri, parts[1]);
search = g_strdup_printf (search_uri, uri_);
else
search = g_strconcat (search_uri, uri_, NULL);
g_free (uri_);
} }
g_strfreev (parts); g_strfreev (parts);
return search; return search;
@ -353,8 +427,10 @@ sokoke_get_desktop (void)
/* Are we running in Xfce? */ /* Are we running in Xfce? */
gint result; gint result;
gchar *out = NULL; gchar *out = NULL;
gchar *err = NULL;
gboolean success = g_spawn_command_line_sync ("xprop -root _DT_SAVE_MODE", 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) if (success && ! result && out != NULL && strstr (out, "xfce4") != NULL)
desktop = SOKOKE_DESKTOP_XFCE; desktop = SOKOKE_DESKTOP_XFCE;
else else
@ -430,34 +506,6 @@ sokoke_xfce_header_new (const gchar* icon,
return NULL; 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* GtkWidget*
sokoke_hig_frame_new (const gchar* title) 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); 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 gboolean
sokoke_key_file_save_to_file (GKeyFile* key_file, sokoke_key_file_save_to_file (GKeyFile* key_file,
const gchar* filename, const gchar* filename,
@ -788,7 +855,7 @@ sokoke_register_stock_items (void)
{ STOCK_STYLES, N_("User_styles"), 0, 0, GTK_STOCK_SELECT_COLOR }, { STOCK_STYLES, N_("User_styles"), 0, 0, GTK_STOCK_SELECT_COLOR },
{ STOCK_TAB_NEW, N_("New _Tab"), 0, 0, GTK_STOCK_ADD }, { STOCK_TAB_NEW, N_("New _Tab"), 0, 0, GTK_STOCK_ADD },
{ STOCK_TRANSFERS, N_("_Transfers"), 0, 0, GTK_STOCK_SAVE }, { 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_USER_TRASH, N_("_Closed Tabs and Windows"), 0, 0, "gtk-undo-ltr" },
{ STOCK_WINDOW_NEW, N_("New _Window"), 0, 0, GTK_STOCK_ADD }, { STOCK_WINDOW_NEW, N_("New _Window"), 0, 0, GTK_STOCK_ADD },
}; };
@ -880,3 +947,119 @@ sokoke_remove_path (const gchar* path,
g_rmdir (path); g_rmdir (path);
return TRUE; 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) 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 This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -15,24 +16,34 @@
#include <katze/katze.h> #include <katze/katze.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <libsoup/soup.h>
#include <JavaScriptCore/JavaScript.h> #include <JavaScriptCore/JavaScript.h>
gchar* gchar*
sokoke_js_script_eval (JSContextRef js_context, sokoke_js_script_eval (JSContextRef js_context,
const gchar* script, const gchar* script,
gchar** exception); gchar** exception);
/* Many themes need this hack for small toolbars to work */ /* Many themes need this hack for small toolbars to work */
#define GTK_ICON_SIZE_SMALL_TOOLBAR GTK_ICON_SIZE_BUTTON #define GTK_ICON_SIZE_SMALL_TOOLBAR GTK_ICON_SIZE_BUTTON
gboolean gboolean
sokoke_spawn_program (const gchar* command, sokoke_show_uri (GdkScreen* screen,
const gchar* argument); 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* gchar*
sokoke_magic_uri (const gchar* uri, sokoke_magic_uri (const gchar* uri,
KatzeArray* search_engines); KatzeArray* search_engines);
typedef enum { typedef enum {
SOKOKE_MENU_POSITION_CURSOR = 0, SOKOKE_MENU_POSITION_CURSOR = 0,
@ -41,97 +52,110 @@ typedef enum {
} SokokeMenuPos; } SokokeMenuPos;
void void
sokoke_combo_box_add_strings (GtkComboBox* combobox, sokoke_combo_box_add_strings (GtkComboBox* combobox,
const gchar* label_first, const gchar* label_first,
...); ...);
void void
sokoke_widget_set_visible (GtkWidget* widget, sokoke_widget_set_visible (GtkWidget* widget,
gboolean visible); gboolean visible);
void void
sokoke_container_show_children (GtkContainer* container); sokoke_container_show_children (GtkContainer* container);
void void
sokoke_widget_popup (GtkWidget* widget, sokoke_widget_popup (GtkWidget* widget,
GtkMenu* menu, GtkMenu* menu,
GdkEventButton* event, GdkEventButton* event,
SokokeMenuPos pos); SokokeMenuPos pos);
GtkWidget* GtkWidget*
sokoke_xfce_header_new (const gchar* icon, sokoke_xfce_header_new (const gchar* icon,
const gchar* title); const gchar* title);
GtkWidget* GtkWidget*
sokoke_superuser_warning_new (void); sokoke_hig_frame_new (const gchar* title);
GtkWidget*
sokoke_hig_frame_new (const gchar* title);
void void
sokoke_widget_set_pango_font_style (GtkWidget* widget, sokoke_widget_set_pango_font_style (GtkWidget* widget,
PangoStyle style); PangoStyle style);
void void
sokoke_entry_set_default_text (GtkEntry* entry, sokoke_entry_set_default_text (GtkEntry* entry,
const gchar* default_text); const gchar* default_text);
gchar* gchar*
sokoke_key_file_get_string_default (GKeyFile* key_file, sokoke_key_file_get_string_default (GKeyFile* key_file,
const gchar* group, const gchar* group,
const gchar* key, const gchar* key,
const gchar* default_value, const gchar* default_value,
GError** error); GError** error);
gint gint
sokoke_key_file_get_integer_default (GKeyFile* key_file, sokoke_key_file_get_integer_default (GKeyFile* key_file,
const gchar* group, const gchar* group,
const gchar* key, const gchar* key,
const gint default_value, const gint default_value,
GError** error); GError** error);
gdouble gdouble
sokoke_key_file_get_double_default (GKeyFile* key_file, sokoke_key_file_get_double_default (GKeyFile* key_file,
const gchar* group, const gchar* group,
const gchar* key, const gchar* key,
gdouble default_value, gdouble default_value,
GError** error); GError** error);
gboolean gboolean
sokoke_key_file_get_boolean_default (GKeyFile* key_file, sokoke_key_file_get_boolean_default (GKeyFile* key_file,
const gchar* group, const gchar* group,
const gchar* key, const gchar* key,
gboolean default_value, gboolean default_value,
GError** error); 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 gboolean
sokoke_key_file_save_to_file (GKeyFile* key_file, sokoke_key_file_save_to_file (GKeyFile* key_file,
const gchar* filename, const gchar* filename,
GError** error); GError** error);
void void
sokoke_widget_get_text_size (GtkWidget* widget, sokoke_widget_get_text_size (GtkWidget* widget,
const gchar* text, const gchar* text,
gint* width, gint* width,
gint* height); gint* height);
GtkWidget* GtkWidget*
sokoke_action_create_popup_menu_item (GtkAction* action); sokoke_action_create_popup_menu_item (GtkAction* action);
GtkWidget* GtkWidget*
sokoke_image_menu_item_new_ellipsized (const gchar* label); sokoke_image_menu_item_new_ellipsized (const gchar* label);
gint64 gint64
sokoke_time_t_to_julian (const time_t* timestamp); sokoke_time_t_to_julian (const time_t* timestamp);
void void
sokoke_register_stock_items (void); sokoke_register_stock_items (void);
const gchar* const gchar*
sokoke_set_config_dir (const gchar* new_config_dir); sokoke_set_config_dir (const gchar* new_config_dir);
gboolean gboolean
sokoke_remove_path (const gchar* path, sokoke_remove_path (const gchar* path,
gboolean ignore_errors); gboolean ignore_errors);
SoupServer*
sokoke_get_res_server (void);
gchar*
sokoke_replace_variables (const gchar* template,
const gchar* variable_first, ...);
#endif /* !__SOKOKE_H__ */ #endif /* !__SOKOKE_H__ */

View file

@ -527,11 +527,11 @@ midori_addons_finalize (GObject* object)
} }
static gboolean static gboolean
_metadata_from_file (const gchar* filename, js_metadata_from_file (const gchar* filename,
GSList** includes, GSList** includes,
GSList** excludes, GSList** excludes,
gchar** name, gchar** name,
gchar** description) gchar** description)
{ {
GIOChannel* channel; GIOChannel* channel;
gboolean found_meta; gboolean found_meta;
@ -601,6 +601,82 @@ _metadata_from_file (const gchar* filename,
return TRUE; 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* static gchar*
_convert_to_simple_regexp (const gchar* pattern) _convert_to_simple_regexp (const gchar* pattern)
{ {
@ -742,6 +818,7 @@ _js_style_from_file (JSContextRef js_context,
error = NULL; error = NULL;
if (g_file_get_contents (filename, &style, NULL, &error)) if (g_file_get_contents (filename, &style, NULL, &error))
{ {
guint meta = 0;
n = strlen (style); n = strlen (style);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
@ -751,7 +828,34 @@ _js_style_from_file (JSContextRef js_context,
/* Change all single quotes to double quotes */ /* Change all single quotes to double quotes */
if (style[i] == '\'') if (style[i] == '\'')
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 ( style_script = g_strdup_printf (
"window.addEventListener ('DOMContentLoaded'," "window.addEventListener ('DOMContentLoaded',"
"function () {" "function () {"
@ -1031,8 +1135,8 @@ midori_addons_update_elements (MidoriAddons* addons)
if (addons->kind == MIDORI_ADDON_USER_SCRIPTS) if (addons->kind == MIDORI_ADDON_USER_SCRIPTS)
{ {
name = NULL; name = NULL;
if (!_metadata_from_file (fullname, &includes, &excludes, if (!js_metadata_from_file (fullname, &includes, &excludes,
&name, &description)) &name, &description))
broken = TRUE; broken = TRUE;
if (name) if (name)
@ -1041,6 +1145,11 @@ midori_addons_update_elements (MidoriAddons* addons)
displayname = name; 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 = g_new (struct AddonElement, 1);
element->name = displayname; element->name = displayname;

View file

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

View file

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

View file

@ -199,7 +199,8 @@ midori_extensions_set_property (GObject* object,
case PROP_APP: case PROP_APP:
{ {
KatzeArray* array; KatzeArray* array;
guint i, n; MidoriExtension* extension;
guint i;
/* FIXME: Handle NULL and subsequent assignments */ /* FIXME: Handle NULL and subsequent assignments */
extensions->app = g_value_get_object (value); extensions->app = g_value_get_object (value);
@ -207,10 +208,9 @@ midori_extensions_set_property (GObject* object,
g_signal_connect (array, "add-item", g_signal_connect (array, "add-item",
G_CALLBACK (midori_extensions_add_item_cb), extensions); G_CALLBACK (midori_extensions_add_item_cb), extensions);
if ((n = katze_array_get_length (array))) i = 0;
for (i = 0; i < n; i++) while ((extension = katze_array_get_nth_item (array, i++)))
midori_extensions_add_item_cb (array, midori_extensions_add_item_cb (array, extension, extensions);
katze_array_get_nth_item (array, i), extensions);
} }
break; break;
default: default:
@ -270,8 +270,11 @@ midori_extensions_treeview_render_text_cb (GtkTreeViewColumn* column,
g_free (name); g_free (name);
g_free (version); g_free (version);
g_free (desc); 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_free (text);
g_object_unref (extension);
} }
static void static void
@ -299,10 +302,12 @@ midori_extensions_treeview_row_activated_cb (GtkTreeView* treeview,
midori_extension_deactivate (extension); midori_extension_deactivate (extension);
else else
g_signal_emit_by_name (extension, "activate", extensions->app); g_signal_emit_by_name (extension, "activate", extensions->app);
gtk_widget_set_sensitive (GTK_WIDGET (button_enable), gtk_widget_set_sensitive (GTK_WIDGET (button_enable),
!midori_extension_is_active (extension)); !midori_extension_is_active (extension));
gtk_widget_set_sensitive (GTK_WIDGET (button_disable), gtk_widget_set_sensitive (GTK_WIDGET (button_disable),
midori_extension_is_active (extension)); 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 static void
midori_history_add_clicked_cb (GtkWidget* toolitem) 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 */ /* FIXME: Take selected folder into account */
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser), midori_browser_edit_bookmark_dialog_new (browser, NULL, TRUE, FALSE);
NULL, TRUE, FALSE);
} }
static void static void
@ -152,11 +151,11 @@ static void
midori_history_clear_clicked_cb (GtkWidget* toolitem, midori_history_clear_clicked_cb (GtkWidget* toolitem,
MidoriHistory* history) MidoriHistory* history)
{ {
GtkWidget* browser; MidoriBrowser* browser;
GtkWidget* dialog; GtkWidget* dialog;
gint result; 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), dialog = gtk_message_dialog_new (GTK_WINDOW (browser),
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
@ -275,7 +274,8 @@ midori_history_clear_cb (KatzeArray* array,
static void static void
midori_history_disconnect_folder (MidoriHistory* history, midori_history_disconnect_folder (MidoriHistory* history,
KatzeArray* array) KatzeArray* array,
gboolean unref)
{ {
KatzeItem* item; KatzeItem* item;
guint i; guint i;
@ -293,8 +293,9 @@ midori_history_disconnect_folder (MidoriHistory* history,
while ((item = katze_array_get_nth_item (array, i++))) while ((item = katze_array_get_nth_item (array, i++)))
{ {
if (KATZE_IS_ARRAY (item)) if (KATZE_IS_ARRAY (item))
midori_history_disconnect_folder (history, KATZE_ARRAY (item)); midori_history_disconnect_folder (history, KATZE_ARRAY (item), unref);
g_object_unref (item); 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_ARRAY (array));
g_return_if_fail (KATZE_IS_ITEM (added_item)); g_return_if_fail (KATZE_IS_ITEM (added_item));
g_return_if_fail (MIDORI_IS_HISTORY (history));
if (KATZE_IS_ARRAY (added_item)) if (KATZE_IS_ARRAY (added_item))
{ {
@ -392,7 +394,7 @@ midori_history_remove_item_cb (KatzeArray* array,
g_assert (KATZE_IS_ITEM (removed_item)); g_assert (KATZE_IS_ITEM (removed_item));
if (KATZE_IS_ARRAY (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)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
midori_history_remove_iter (model, NULL, removed_item); 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_remove_item_cb (array, item, history);
} }
midori_history_disconnect_folder (history, array); midori_history_disconnect_folder (history, array, TRUE);
} }
static void static void
@ -459,7 +461,6 @@ midori_history_insert_item (MidoriHistory* history,
age = day - pday; age = day - pday;
gtk_tree_store_insert_with_values (treestore, &iter, parent, gtk_tree_store_insert_with_values (treestore, &iter, parent,
0, 0, item, 1, age, -1); 0, 0, item, 1, age, -1);
g_object_unref (item);
piter = &iter; piter = &iter;
} }
i = 0; i = 0;
@ -481,7 +482,7 @@ midori_history_set_app (MidoriHistory* history,
if (history->array) if (history->array)
{ {
midori_history_disconnect_folder (history, history->array); midori_history_disconnect_folder (history, history->array, TRUE);
g_object_unref (history->array); g_object_unref (history->array);
model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
gtk_tree_store_clear (GTK_TREE_STORE (model)); 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)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (history->treeview));
midori_history_insert_item (history, GTK_TREE_STORE (model), 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) else if (age > 6)
{ {
sdate = g_strdup_printf (_("A week ago")); sdate = _("A week ago");
g_object_set (renderer, "text", sdate, NULL); g_object_set (renderer, "text", sdate, NULL);
g_free (sdate);
} }
else if (age > 1) 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_object_set (renderer, "text", sdate, NULL);
g_free (sdate); g_free (sdate);
} }
@ -641,10 +642,10 @@ midori_history_row_activated_cb (GtkTreeView* treeview,
uri = katze_item_get_uri (item); uri = katze_item_get_uri (item);
if (uri && *uri) if (uri && *uri)
{ {
GtkWidget* browser; MidoriBrowser* browser;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history)); browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri); midori_browser_set_current_uri (browser, uri);
} }
g_object_unref (item); g_object_unref (item);
@ -691,8 +692,8 @@ midori_history_open_activate_cb (GtkWidget* menuitem,
if (uri && *uri) if (uri && *uri)
{ {
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (history)); MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_set_current_uri (MIDORI_BROWSER (browser), uri); 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) if ((uri = katze_item_get_uri (child)) && *uri)
{ {
GtkWidget* browser; MidoriBrowser* browser;
MidoriWebSettings* settings; MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history)); browser = midori_browser_get_for_widget (GTK_WIDGET (history));
n = midori_browser_add_item (MIDORI_BROWSER (browser), child); n = midori_browser_add_item (browser, child);
settings = katze_object_get_object (browser, "settings"); settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background")) if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n); midori_browser_set_current_page (browser, n);
g_object_unref (settings); g_object_unref (settings);
} }
i++; i++;
@ -731,14 +732,14 @@ midori_history_open_in_tab_activate_cb (GtkWidget* menuitem,
{ {
if ((uri = katze_item_get_uri (item)) && *uri) if ((uri = katze_item_get_uri (item)) && *uri)
{ {
GtkWidget* browser; MidoriBrowser* browser;
MidoriWebSettings* settings; MidoriWebSettings* settings;
browser = gtk_widget_get_toplevel (GTK_WIDGET (history)); browser = midori_browser_get_for_widget (GTK_WIDGET (history));
n = midori_browser_add_item (MIDORI_BROWSER (browser), item); n = midori_browser_add_item (browser, item);
settings = katze_object_get_object (browser, "settings"); settings = katze_object_get_object (browser, "settings");
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background")) if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
midori_browser_set_current_page (MIDORI_BROWSER (browser), n); midori_browser_set_current_page (browser, n);
g_object_unref (settings); g_object_unref (settings);
} }
} }
@ -756,7 +757,7 @@ midori_history_open_in_window_activate_cb (GtkWidget* menuitem,
if (uri && *uri) 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); g_signal_emit_by_name (browser, "new-window", uri);
} }
} }
@ -773,8 +774,8 @@ midori_history_bookmark_activate_cb (GtkWidget* menuitem,
if (uri && *uri) if (uri && *uri)
{ {
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (history)); MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (history));
midori_browser_edit_bookmark_dialog_new (MIDORI_BROWSER (browser), item, TRUE, FALSE); 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) if (uri && *uri)
{ {
GtkWidget* browser; MidoriBrowser* browser;
gint n; gint n;
browser = gtk_widget_get_toplevel (widget); browser = midori_browser_get_for_widget (widget);
n = midori_browser_add_uri (MIDORI_BROWSER (browser), uri); n = midori_browser_add_uri (browser, uri);
midori_browser_set_current_page (MIDORI_BROWSER (browser), n); midori_browser_set_current_page (browser, n);
} }
} }
else else
@ -967,6 +968,10 @@ midori_history_finalize (GObject* object)
if (history->app) if (history->app)
g_object_unref (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); g_object_unref (history->array);
} }

View file

@ -84,7 +84,7 @@ midori_plugins_class_init (MidoriPluginsClass* class)
static const gchar* static const gchar*
midori_plugins_get_label (MidoriViewable* viewable) midori_plugins_get_label (MidoriViewable* viewable)
{ {
return _("Plugins"); return _("Netscape plugins");
} }
static const gchar* static const gchar*
@ -277,6 +277,10 @@ midori_plugins_init (MidoriPlugins* plugins)
const gchar* plugin_name; const gchar* plugin_name;
const gchar* plugin_description; 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); fullname = g_build_filename (plugin_path, filename, NULL);
module = g_module_open (fullname, G_MODULE_BIND_LOCAL); module = g_module_open (fullname, G_MODULE_BIND_LOCAL);
g_free (fullname); g_free (fullname);

View file

@ -100,8 +100,23 @@ midori_transfers_button_clear_clicked_cb (GtkToolItem* toolitem,
{ {
GtkTreeModel* model = gtk_tree_view_get_model ( GtkTreeModel* model = gtk_tree_view_get_model (
GTK_TREE_VIEW (transfers->treeview)); GTK_TREE_VIEW (transfers->treeview));
/* FIXME: Clear only finished and cancelled downloads */ GtkTreeIter iter;
gtk_tree_store_clear (GTK_TREE_STORE (model)); 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* static GtkWidget*
@ -173,8 +188,8 @@ midori_transfers_browser_add_download_cb (MidoriBrowser* browser,
treeview = GTK_TREE_VIEW (transfers->treeview); treeview = GTK_TREE_VIEW (transfers->treeview);
model = gtk_tree_view_get_model (treeview); model = gtk_tree_view_get_model (treeview);
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
NULL, NULL, G_MAXINT, NULL, G_MAXINT,
0, NULL, 1, download, -1); 0, NULL, 1, download, -1);
g_signal_connect (download, "notify::progress", g_signal_connect (download, "notify::progress",
G_CALLBACK (midori_transfers_download_notify_progress_cb), transfers); 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: case WEBKIT_DOWNLOAD_STATUS_FINISHED:
{ {
const gchar* uri; const gchar* uri;
gboolean success;
uri = webkit_download_get_destination_uri (download); 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); 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; break;
} }
case WEBKIT_DOWNLOAD_STATUS_CANCELLED: case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
@ -354,10 +362,13 @@ midori_transfers_hierarchy_changed_cb (MidoriTransfers* transfers,
GtkWidget* old_parent) GtkWidget* old_parent)
{ {
#if WEBKIT_CHECK_VERSION (1, 1, 3) #if WEBKIT_CHECK_VERSION (1, 1, 3)
GtkWidget* browser = gtk_widget_get_toplevel (GTK_WIDGET (transfers)); MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (transfers));
if (GTK_WIDGET_TOPLEVEL (browser)) if (MIDORI_IS_BROWSER (browser))
g_signal_connect (browser, "add-download", g_signal_connect (browser, "add-download",
G_CALLBACK (midori_transfers_browser_add_download_cb), transfers); 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 #endif
} }
@ -368,7 +379,7 @@ midori_transfers_init (MidoriTransfers* transfers)
GtkTreeViewColumn* column; GtkTreeViewColumn* column;
GtkCellRenderer* renderer_pixbuf; GtkCellRenderer* renderer_pixbuf;
GtkCellRenderer* renderer_text; 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)); transfers->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (treestore));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (transfers->treeview), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (transfers->treeview), FALSE);
column = gtk_tree_view_column_new (); column = gtk_tree_view_column_new ();

View file

@ -1,2 +1,2 @@
# set of available languages (in alphabetic order) # 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-item.c
katze/katze-array.c katze/katze-array.c
katze/katze-arrayaction.c katze/katze-arrayaction.c
extensions/adblock.c
extensions/colorful-tabs.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/mouse-gestures/main.c
extensions/page-holder.c extensions/page-holder.c
extensions/statusbar-features.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; GtkAction* action = actions->data;
if (g_strcmp0 (gtk_action_get_name (action), "WindowClose")) if (g_strcmp0 (gtk_action_get_name (action), "WindowClose"))
if (g_strcmp0 (gtk_action_get_name (action), "EncodingCustom")) 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); actions = g_list_next (actions);
} }
g_list_free (actions); g_list_free (actions);

View file

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

View file

@ -75,6 +75,21 @@ extension_mock_object (void)
return extension; 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 static void
extension_settings (void) extension_settings (void)
{ {
@ -83,6 +98,9 @@ extension_settings (void)
gboolean nihilist; gboolean nihilist;
gint age; gint age;
const gchar* lastname; const gchar* lastname;
gchar* pet_names[] = {"Tiger", "Bonzo", "Streuner", NULL};
gchar** names;
gsize names_n;
app = midori_app_new (); app = midori_app_new ();
extension = extension_mock_object (); extension = extension_mock_object ();
@ -126,6 +144,27 @@ extension_settings (void)
lastname = midori_extension_get_string (extension, "lastname"); lastname = midori_extension_get_string (extension, "lastname");
g_assert_cmpstr (lastname, ==, "Theodor Fontane"); g_assert_cmpstr (lastname, ==, "Theodor Fontane");
midori_extension_deactivate (extension); 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 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 ("http://example.com", "http://example.com");
test_input ("example.com", "http://example.com"); test_input ("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"); test_input ("/home/user/midori.html", "file:///home/user/midori.html");
a = g_get_current_dir (); a = g_get_current_dir ();
b = g_strconcat ("file://", a, G_DIR_SEPARATOR_S, "magic-uri.c", NULL); 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 ("g conference \"April 2, 7:00 am\"", NULL);
test_input ("max@mustermann.de", NULL); test_input ("max@mustermann.de", NULL);
test_input ("g max@mustermann.de", NULL); test_input ("g max@mustermann.de", NULL);
test_input ("g inurl:http://twotoasts.de bug", NULL);
} }
static void static void

19
wscript
View file

@ -22,7 +22,7 @@ import UnitTest
major = 0 major = 0
minor = 1 minor = 1
micro = 6 micro = 7
APPNAME = 'midori' APPNAME = 'midori'
VERSION = str (major) + '.' + str (minor) + '.' + str (micro) VERSION = str (major) + '.' + str (minor) + '.' + str (micro)
@ -212,7 +212,6 @@ def configure (conf):
print "User documentation: " + user_docs + " (docutils)" print "User documentation: " + user_docs + " (docutils)"
print "API documentation: " + api_docs + " (gtk-doc)" print "API documentation: " + api_docs + " (gtk-doc)"
print print
print "Optional run time dependencies:"
print "Single instance: " + unique + " (unique)" print "Single instance: " + unique + " (unique)"
if unique == 'yes' and conf.check_cfg (modversion='unique-1.0') == '1.0.4': 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.') 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 ' + \ ' -o ' + blddir + '/data/logo-shade.png ' + \
srcdir + '/data/logo-shade.svg' srcdir + '/data/logo-shade.svg'
if not Utils.exec_command (command): 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: else:
Utils.pprint ('BLUE', "logo-shade could not be rasterized.") 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']: if Options.commands['check']:
bld.add_subdirs ('tests') bld.add_subdirs ('tests')
@ -393,8 +396,12 @@ def shutdown ():
elif Options.options.run: elif Options.options.run:
folder = os.path.dirname (Build.bld.env['waf_config_files'][0]) folder = os.path.dirname (Build.bld.env['waf_config_files'][0])
try: try:
ext = 'MIDORI_EXTENSION_PATH=' + folder + os.sep + 'extensions' relfolder = os.path.relpath (folder)
nls = 'NLSPATH=' + folder + os.sep + 'po' except:
pass
try:
ext = 'MIDORI_EXTENSION_PATH=' + relfolder + os.sep + 'extensions'
nls = 'NLSPATH=' + relfolder + os.sep + 'po'
lang = os.environ['LANG'] lang = os.environ['LANG']
try: try:
for lang in os.listdir (folder + os.sep + 'po'): for lang in os.listdir (folder + os.sep + 'po'):
@ -410,7 +417,7 @@ def shutdown ():
'LC_MESSAGES' + os.sep + APPNAME + '.mo') 'LC_MESSAGES' + os.sep + APPNAME + '.mo')
except: except:
pass pass
command = folder + os.sep + APPNAME + os.sep + APPNAME command = relfolder + os.sep + APPNAME + os.sep + APPNAME
print ext + ' ' + nls + ' ' + command print ext + ' ' + nls + ' ' + command
Utils.exec_command (ext + ' ' + nls + ' ' + command) Utils.exec_command (ext + ' ' + nls + ' ' + command)
except: except: