--- /dev/null
+#!/usr/bin/env python
+# coding=UTF-8
+#
+# Copyright (C) 2010 Stefanos Harhalakis
+#
+# This file is part of wifieye.
+#
+# wifieye is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# wifieye is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with wifieye. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
+
+__version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
+
+import gtk
+import gobject
+import hildon
+import time
+import copy
+
+from hildon import StackableWindow
+#from portrait import FremantleRotation
+#from xdg.IconTheme import getIconPath
+
+import config
+import apps
+from icon import Icon, getIcon
+from iconw import IconWidget
+from icongrid import IconGridWidget
+from about import DlgAbout
+from portrait import FremantleRotation
+from sig import Disconnector
+
+class DialogIconSize(gtk.Dialog):
+ def __init__(self, config):
+ gtk.Dialog.__init__(self, "Adjust icon size")
+
+ # Operate on copy of config
+ self.config=copy.copy(config)
+ # And keep another copy to be able to undo
+ self.config0=copy.copy(config)
+
+ vbox=self.vbox
+
+ self.pa=hildon.PannableArea()
+ vbox.add(self.pa)
+ self.pa.set_property('mov-mode', hildon.MOVEMENT_MODE_VERT)
+
+ vbox=gtk.VBox()
+ self.pa.add_with_viewport(vbox)
+ self.pa.set_size_request(-1, 1000)
+
+ self.labelDim=gtk.Label("koko")
+ vbox.add(self.labelDim)
+
+ vbox.add(gtk.Label("Icon size:"))
+ scale=gtk.HScale()
+ scale.set_digits(0)
+ scale.set_value_pos(gtk.POS_RIGHT)
+ scale.set_range(*self.config.getIconSizeRange())
+ scale.set_increments(8, 16)
+ self.scaleIconSize=scale
+ vbox.add(scale)
+
+ hbox=gtk.HBox()
+ vbox.add(hbox)
+
+ vbox2=gtk.VBox()
+ vbox2.add(gtk.Label("Padding:"))
+ scale=gtk.HScale()
+ scale.set_digits(0)
+ scale.set_value_pos(gtk.POS_RIGHT)
+ scale.set_range(*self.config.getIconPaddingRange())
+ scale.set_increments(2,2)
+ self.scaleIconPadding=scale
+ vbox2.add(scale)
+ hbox.add(vbox2)
+
+ vbox2=gtk.VBox()
+ vbox2.add(gtk.Label("Margin:"))
+ scale=gtk.HScale()
+ scale.set_digits(0)
+ scale.set_value_pos(gtk.POS_RIGHT)
+ scale.set_range(*self.config.getIconMarginRange())
+ scale.set_increments(2,2)
+ self.scaleIconMargin=scale
+ vbox2.add(scale)
+ hbox.add(vbox2)
+
+ self.icon=IconWidget(False, self.config)
+ self.icon.setApp('osso-addressbook')
+ vbox.add(self.icon)
+
+ self.scaleIconSize.set_value(self.config.getIconSize())
+ self.scaleIconPadding.set_value(self.config.getIconPadding())
+ self.scaleIconMargin.set_value(self.config.getIconMargin())
+
+ # Set thos after settings initial values to avoid unwanted effects
+ self.scaleIconSize.connect('value-changed', self.slotSzChangeSize)
+ self.scaleIconPadding.connect('value-changed', self.slotSzChangePadding)
+ self.scaleIconMargin.connect('value-changed', self.slotSzChangeMargin)
+
+ hbox=gtk.HBox()
+ vbox.add(hbox)
+
+ but=hildon.Button(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ but.set_title("OK")
+ but.connect("clicked", self.slotButtonOK)
+ #hbox.add(but)
+ self.action_area.pack_start(but)
+ self.buttonOK=but
+
+ but=hildon.Button(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ but.set_title("Undo")
+ but.connect("clicked", self.slotButtonUndo)
+ #hbox.add(but)
+ self.action_area.pack_start(but)
+ self.buttonUndo=but
+
+ but=hildon.Button(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ but.set_title("Restore defaults")
+ but.connect("clicked", self.slotButtonDefaults)
+ #hbox.add(but)
+ self.action_area.pack_start(but)
+ self.buttonDefaults=but
+
+ self.show_all()
+
+ self.recalc()
+
+ def setWH(self, w, h):
+ self.labelDim.set_text("Grid size: %d x %d icons" % (w, h))
+ #self.labelWidth.set_text("Width: %d icons" % w)
+ #self.labelHeight.set_text("Height: %d icons" % h)
+
+ def recalc(self):
+ maxsz=self.config.getMaxSize()
+ self.setWH(maxsz[0], maxsz[1])
+ self.icon.refresh()
+
+ def getIconSize(self):
+ return(self.scaleIconSize.get_value())
+
+ def setIconSize(self, iconsize):
+ self.scaleIconSize.set_value(iconsize)
+
+ def getIconPadding(self):
+ return(self.scaleIconPadding.get_value())
+
+ def setIconPadding(self, pad):
+ self.scaleIconPadding.set_value(pad)
+
+ def getIconMargin(self):
+ return(self.scaleIconMargin.get_value())
+
+ def setIconMargin(self, margin):
+ self.scaleIconMargin.set_value(margin)
+
+ def slotSzChangeSize(self, sender):
+ self.config.setIconSize(int(self.getIconSize()))
+ self.recalc()
+
+ def slotSzChangeMargin(self, sender):
+ self.config.setIconMargin(int(self.getIconMargin()))
+ self.recalc()
+
+ def slotSzChangePadding(self, sender):
+ self.config.setIconPadding(int(self.getIconPadding()))
+ self.recalc()
+
+ def slotButtonUndo(self, sender):
+ self.config=copy.copy(self.config0)
+ self.recalc()
+ self.resetSliders()
+
+ def slotButtonDefaults(self, sender):
+ self.config.setDefaultSizes()
+ self.recalc()
+ self.resetSliders()
+
+ def slotButtonOK(self, sender):
+ self.response(gtk.RESPONSE_OK)
+
+ def resetSliders(self):
+ self.scaleIconSize.set_value(self.config.getIconSize())
+ self.scaleIconPadding.set_value(self.config.getIconPadding())
+ self.scaleIconMargin.set_value(self.config.getIconMargin())
+
+ def getData(self):
+ ret={
+ 'size': self.config.getIconSize(),
+ 'padding': self.config.getIconPadding(),
+ 'margin': self.config.getIconMargin()
+ }
+
+ return(ret)
+
+
+class WinConfig(StackableWindow, Disconnector): #, FremantleRotation):
+ def __init__(self, config, *args):
+ StackableWindow.__init__(self)
+ Disconnector.__init__(self)
+# FremantleRotation.__init__(self, "DrlaunchPlugin",
+# mode=FremantleRotation.AUTOMATIC)
+
+ self.config=copy.copy(config)
+
+ self.setupUi()
+
+# h=self.c(self, 'delete-event', self.slotDeleteEvent)
+
+# This is a nice test. If it is displayed then the window is actually
+# destroyed and there is no memory leak
+# def __del__(self):
+# print "wc-del"
+
+ def setupUi(self):
+ """
+ self.pa Main Pannable Area
+ self.col1 A VBox for the first column
+ self.col2 A VBox for the second column
+ self.w_igw The IGW in an alignment
+ """
+ self.set_title("DrLaunch v" + config.version)
+
+ self.pa=hildon.PannableArea()
+# self.add(self.pa)
+ self.pa.set_property('mov-mode', hildon.MOVEMENT_MODE_HORIZ)
+
+ self.add(self.pa)
+
+#1 hbox=gtk.HBox()
+#1 self.pa.add_with_viewport(hbox)
+
+ # Add the first column of options
+ al=gtk.Alignment(yscale=1)
+#1 hbox.add(al)
+
+ vbox=gtk.VBox()
+ al.add(vbox)
+ self.col1=al
+
+ maxsz=self.config.getMaxSize()
+
+ self.maxmaxsz=(16,16)
+
+ # ----------------------------------------------
+ vbox.add(gtk.Label('Width:'))
+
+ hbox2=gtk.HBox()
+ vbox.add(hbox2)
+
+ self.butsSizeX=[]
+ for i in xrange(self.maxmaxsz[0]):
+ but=hildon.GtkToggleButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("%s" % (i+1,))
+ self.c(but, 'toggled', self.slotButtonSizeX, i)
+
+ self.butsSizeX.append(but)
+
+ hbox2.add(but)
+
+ # ----------------------------------------------
+ vbox.add(gtk.Label('Height:'))
+
+ hbox2=gtk.HBox()
+ vbox.add(hbox2)
+
+ self.butsSizeY=[]
+ for i in xrange(self.maxmaxsz[1]):
+ but=hildon.GtkToggleButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("%s" % (i+1,))
+ self.c(but, 'toggled', self.slotButtonSizeY, i)
+
+ self.butsSizeY.append(but)
+
+ hbox2.add(but)
+
+ vbox2=gtk.VBox()
+
+ al=gtk.Alignment(xalign=0, yalign=1, xscale=1)
+ al.add(vbox2)
+ self.col11=al
+
+ vbox.add(al)
+ vbox=vbox2
+
+ but=hildon.CheckButton(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("Rotate icons individually")
+ self.c(but, 'toggled', self.slotButtonRotateIndividually)
+ self.buttonRotateIndividually=but
+ vbox.add(but)
+
+ but=hildon.CheckButton(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("Require long press")
+# but.connect('toggled', self.slotButtonLongpress)
+ self.buttonRequireLongpress=but
+ vbox.add(but)
+
+ but=hildon.CheckButton(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("Animate rotation")
+ self.buttonAnimateRotation=but
+ vbox.add(but)
+
+ # -----------------------------------------------
+ # Second column of options
+ vbox=gtk.VBox()
+
+ al=gtk.Alignment(xalign=0, yalign=1, xscale=1)
+ al.add(vbox)
+ self.col2=al
+
+ but=hildon.CheckButton(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("Theme background")
+ but.connect('toggled', self.slotButtonThemeBackground)
+ self.buttonThemeBackground=but
+ vbox.add(but)
+
+ but=hildon.CheckButton(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
+ but.set_label("No background")
+ self.c(but, 'toggled', self.slotButtonNoBackground)
+ self.buttonNoBackground=but
+ vbox.add(but)
+
+ but=hildon.Button(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ but.set_label("Adjust icon size")
+ self.c(but, 'clicked', self.slotButtonIconSize)
+ self.buttonIconSize=but
+ vbox.add(but)
+
+#1 hbox.add(al)
+ but=hildon.Button(
+ gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ but.set_title("About")
+ self.c(but, "clicked", self.slotButtonAbout)
+ vbox.add(but)
+ self.buttonAbout=but
+
+ # -----------------------------------------------
+ # Add the icongrid
+ self.igw=IconGridWidget(True, self.config, False)
+# self.igw.do_realize()
+# self.igw.setSize(config.getSize())
+ al=gtk.Alignment(xalign=0, xscale=0)
+ al.add(self.igw)
+ al.set_padding(0, 0, 20, 0)
+ self.w_igw=al
+#1 hbox.add(al)
+
+ self.igw.connect('long-press', self.slotLongpress)
+ self.igw.connect('click', self.slotLongpress)
+
+ self.ignore_toggle=False
+
+ self.setSize(self.config.getSize())
+ self.setIndiv(self.config.getIndiv())
+ self.setLongpress(self.config.getLongpress())
+ self.setAnimate(self.config.getAnimate())
+ self.setNoBg(self.config.getNoBg())
+ self.setThemeBg(self.config.getThemeBg())
+# self.setIconSize(self.config.getIconSize())
+# self.setIconPadding(self.config.getIconPadding())
+# self.setIconMargin(self.config.getIconMargin())
+
+ hbox=gtk.HBox()
+ hbox.add(self.col1)
+ hbox.add(self.col2)
+ hbox.add(self.w_igw)
+ self.pa.add_with_viewport(hbox)
+
+ self.adjustMaxSize()
+
+# def setupUi(self, orientation):
+#
+# self.setupUi0()
+#
+# hbox=gtk.HBox()
+#
+# if orientation=='l':
+# hbox.add(self.col1)
+# hbox.add(self.col2)
+# hbox.add(self.w_igw)
+# else:
+# vbox=gtk.VBox()
+# hbox.add(vbox)
+# vbox.add(self.col1)
+# vbox.add(self.col2)
+# hbox.add(self.w_igw)
+#
+# self.pa.add_with_viewport(hbox)
+#
+# if self.get_child()!=None:
+# self.remove(self.get_child())
+# self.add(self.pa)
+# self.pa.show_all()
+
+ def adjustMaxSize(self):
+ sz=self.config.getMaxSize()
+ maxsz=self.maxmaxsz
+
+ for x in xrange(maxsz[0]):
+ if x<sz[0]:
+ self.butsSizeX[x].show()
+ else:
+ self.butsSizeX[x].hide()
+
+ for y in xrange(maxsz[1]):
+ if y<sz[1]:
+ self.butsSizeY[y].show()
+ else:
+ self.butsSizeY[y].hide()
+
+ osz=self.getSize()
+ nsz=[osz[0], osz[1]]
+
+ if osz[0]>sz[0]:
+ nsz[0]=sz[0]
+ if osz[1]>sz[1]:
+ nsz[1]=sz[1]
+ self.setSize(nsz)
+
+ self.setIndiv(self.getIndiv())
+
+ def setLayoutPortrait(self):
+ print "lo: p"
+ hbox=gtk.HBox()
+
+ vbox=gtk.VBox()
+ hbox.add(vbox)
+ self.col1.reparent(vbox)
+ self.col2.reparent(vbox)
+ self.w_igw.reparent(hbox)
+
+ r=self.pa.get_children()[0]
+ self.pa.remove(r)
+ r.destroy()
+ self.pa.add_with_viewport(hbox)
+
+ self.pa.show_all()
+
+ def setLayoutLandscape(self):
+ print "lo: l"
+ hbox=gtk.HBox()
+
+ self.col1.reparent(hbox)
+ self.col2.reparent(hbox)
+ self.w_igw.reparent(hbox)
+
+ r=self.pa.get_children()[0]
+ self.pa.remove(r)
+ r.destroy()
+ self.pa.add_with_viewport(hbox)
+
+ self.pa.show_all()
+
+ def on_orientation_changed(self, orientation):
+ # This is disabled for now since I've not found any reliable
+ # way for supporting orientation changes (#$#%$#*&% GTK)
+ return
+
+ print "orch:", orientation
+ if orientation=='portrait':
+ self.setLayoutPortrait()
+ else:
+ self.setLayoutLandscape()
+
+# def slotDeleteEvent(self, sender, event):
+# print "wc-del-event"
+# return(False)
+
+ def slotLongpress(self, sender, icon):
+ self.doConfig(icon)
+
+ def slotButtonSizeX(self, sender, id):
+ if self.getIndiv():
+ old=self.getSize()
+ sz=(id+1, old[1])
+ else:
+ sz=(id+1, id+1)
+
+ self.setSize(sz)
+
+ def slotButtonSizeY(self, sender, id):
+ old=self.getSize()
+ sz=(old[0], id+1)
+ self.setSize(sz)
+
+ def slotButtonRotateIndividually(self, sender):
+ but=self.buttonRotateIndividually
+ self.setIndiv(but.get_active())
+
+ def slotButtonNoBackground(self, sender):
+ nobg=self.getNoBg()
+ self.setNoBg(nobg)
+
+ def slotButtonThemeBackground(self, sender):
+ themebg=self.getThemeBg()
+ self.setThemeBg(themebg)
+
+ def slotButtonAbout(self, sender):
+ DlgAbout.present2(self)
+
+ def slotButtonIconSize(self, sender):
+ dlg=DialogIconSize(self.config)
+ ret=dlg.run()
+ dt=dlg.getData()
+ dlg.destroy()
+
+ if ret!=gtk.RESPONSE_OK:
+ return
+
+ self.config.setIconSize(dt['size'])
+ self.config.setIconMargin(dt['margin'])
+ self.config.setIconPadding(dt['padding'])
+ self.igw.reconfig()
+ self.adjustMaxSize()
+ self.queue_draw()
+
+ def show_all(self):
+ StackableWindow.show_all(self)
+# return
+ self.adjustMaxSize()
+ self.queue_draw()
+
+# def slotScaleIconSzChange(self, sender):
+# return
+# self.config.setIconSize(self.getIconSize())
+# self.config.setIconMargin(self.getIconMargin())
+# self.config.setIconPadding(self.getIconPadding())
+# self.igw.reconfig()
+# self.adjustMaxSize()
+# self.queue_draw()
+
+# def slotButtonLongpress(self, sender):
+# but=self.buttonRequireLongpress
+# self.set
+
+ def setSize(self, sz):
+ if self.ignore_toggle:
+ return
+
+ self.ignore_toggle=True
+
+ maxsz=self.config.getMaxSize()
+
+ id=sz[0]-1
+
+ for i in xrange(maxsz[0]):
+ but=self.butsSizeX[i]
+ but.set_active(i==id)
+
+ id=sz[1]-1
+
+ for i in xrange(maxsz[1]):
+ but=self.butsSizeY[i]
+ but.set_active(i==id)
+
+ self.ignore_toggle=False
+
+ self.igw.setSize(sz)
+
+ self.igw.queue_draw()
+ self.queue_draw()
+
+ def getSize(self):
+ return(self.igw.getSize())
+
+ def getIndiv(self):
+ ret=self.buttonRotateIndividually.get_active()
+
+ return(ret)
+
+ def setIndiv(self, indiv):
+ if indiv:
+ for i in self.butsSizeY:
+ i.set_sensitive(True)
+ for i in self.butsSizeX:
+ i.set_sensitive(True)
+
+ else:
+ sz=self.config.getMaxSize()
+
+ for i in self.butsSizeY:
+ i.set_sensitive(False)
+
+ cnt=0
+ for i in self.butsSizeX:
+ cnt+=1
+ # Height is always the narrower, so use that as a limit
+ if cnt>sz[1]:
+ i.set_sensitive(False)
+ else:
+ i.set_sensitive(True)
+
+ sz=self.getSize()
+ szx=sz[0]
+ if szx>sz[1]:
+ szx=sz[1]
+ self.setSize((szx, szx))
+
+ self.buttonRotateIndividually.set_active(indiv)
+
+ def setLongpress(self, lp):
+ self.buttonRequireLongpress.set_active(lp)
+
+ def setAnimate(self, ar):
+ self.buttonAnimateRotation.set_active(ar)
+
+ def getNoBg(self):
+ ret=self.buttonNoBackground.get_active()
+ return(ret)
+
+ def setNoBg(self, nobg):
+ self.buttonNoBackground.set_active(nobg)
+
+ self.buttonThemeBackground.set_sensitive(not nobg)
+ self.config.setNoBg(nobg)
+ self.igw.reconfig()
+
+ def getThemeBg(self):
+ ret=self.buttonThemeBackground.get_active()
+ return(ret)
+
+ def setThemeBg(self, themebg):
+ self.buttonThemeBackground.set_active(themebg)
+ self.config.setThemeBg(themebg)
+ self.igw.reconfig()
+
+ def doConfig(self, icon):
+ aps=apps.scan()
+
+ lst=[aps[x]['name'] for x in aps]
+ lst.sort()
+
+ dialog=gtk.Dialog('App select', None,
+ gtk.DIALOG_DESTROY_WITH_PARENT, buttons=())
+
+ selector=hildon.TouchSelectorEntry(text=True)
+ selector.set_column_selection_mode(
+ hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
+
+ dialog.vbox.pack_start(selector, True, True, 0)
+ dialog.set_size_request(0,900)
+ dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+ dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
+
+ selector.append_text('None')
+
+ idx=0
+ cnt=1
+ for app in lst:
+ if app==None:
+ continue
+ selector.append_text(app)
+ if icon.appname!=None and aps[icon.appname]['name']==app:
+ idx=cnt
+ cnt+=1
+
+ selector.set_active(0, idx)
+
+ dialog.show_all()
+
+ app=None
+
+ r=dialog.run()
+
+ if r==gtk.RESPONSE_OK:
+ idx2=selector.get_active(0)
+ if idx2<1:
+ app=None
+ else:
+ cur=lst[idx2-1]
+ for i in aps:
+ if aps[i]['name']==cur:
+ app=aps[i]
+ break
+ if app!=None:
+ app['icon2']=getIcon(app['icon'], self.config.getIconSize())
+ else:
+ app={
+ 'id': None,
+ 'icon2': None,
+ }
+ icon.setApp(app)
+
+ dialog.destroy()
+
+# def finish(self):
+# print "wc-finish"
+# self.igw=None
+#
+# self.dis_finish()
+
+ def getData(self):
+ szx=0
+ szy=0
+ for but in self.butsSizeX:
+ szx+=1
+ if but.get_active()==True:
+ break
+
+ for but in self.butsSizeY:
+ szy+=1
+ if but.get_active()==True:
+ break
+
+ if self.getIndiv():
+ sz=(szx, szy)
+ else:
+ sz=(szx, szx)
+
+ wapps={}
+
+ for x in xrange(sz[0]):
+ for y in xrange(sz[1]):
+ ico=self.igw.get(x,y)
+ k=(x,y)
+ wapps[k]=ico.appname
+
+ indiv=self.buttonRotateIndividually.get_active()
+ lp=self.buttonRequireLongpress.get_active()
+ ar=self.buttonAnimateRotation.get_active()
+ nobg=self.buttonNoBackground.get_active()
+ themebg=self.buttonThemeBackground.get_active()
+
+ ret={
+ 'size': sz,
+ 'apps': wapps,
+ 'indiv': indiv,
+ 'longpress': lp,
+ 'animate': ar,
+ 'nobg': nobg,
+ 'themebg': themebg,
+ 'iconsize': self.config.getIconSize(),
+ 'iconpadding': self.config.getIconPadding(),
+ 'iconmargin': self.config.getIconMargin(),
+ }
+
+ return(ret)
+
+if __name__=="__main__":
+ win=WinConfig()
+ win.connect('delete-event', gtk.main_quit)
+
+ win.show_all()
+ gtk.main()
+
+
+
+# vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
+