;;; ide-skel.el --- IDE skeleton for Emacs Lisp hackers

;; Copyright (C) 2008 Peter Karpiuk, Scott Tiger S.A.

;; Author: Peter Karpiuk <piotr.karpiuk (at) gmail (dot) com>
;; Maintainer: Peter Karpiuk <piotr.karpiuk (at) gmail (dot) com>
;; Created: 24 Apr 2008
;; Version 0.6.0
;; Keywords: ide speedbar

;; GNU Emacs 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 2, or (at your
;; option) any later version.

;; GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;;; Commentary:

;;   Ide-skel is a skeleton (or framework) of IDE for Emacs users.
;;   Like Eclipse, it can be used as is with some predefined plugins
;;   on board, but is designed to extend by Emacs Lisp programmers to
;;   suite their own needs.  Emacs 22 only, tested under Linux only
;;   (under Windows ide-skel.el will rather not work, sorry).
;;
;; ** Configuration in .emacs
;;
;;     (require 'ide-skel)
;;
;;     ;; optional, but useful - see Emacs Manual
;;     (partial-completion-mode)
;;     (icomplete-mode)
;;
;;     ;; for convenience
;;     (global-set-key [f4] 'ide-skel-proj-find-files-by-regexp)
;;     (global-set-key [f5] 'ide-skel-proj-grep-files-by-regexp)
;;     (global-set-key [f10] 'ide-skel-toggle-left-view-window)
;;     (global-set-key [f11] 'ide-skel-toggle-bottom-view-window)
;;     (global-set-key [f12] 'ide-skel-toggle-right-view-window)
;;     (global-set-key [C-next] 'tabbar-backward)
;;     (global-set-key [C-prior]  'tabbar-forward)
;;
;; ** Side view windows
;;
;;   Left and right view windows are "speedbars" - they are embedded
;;   inside main Emacs frame and can be open/closed independently.
;;   Right view window summarizes information related to the current
;;   editor buffer - it can present content of such buffer in another
;;   way (eg. Imenu tree), or show an extra panel for buffer major
;;   mode operations (see SQL*Plus mode plugin example).  Left view
;;   window contains buffers such like buffer list (yet another,
;;   popular way for switching buffers), filesystem/project browser
;;   for easy navigation, or Info documentation browser,
;;   or... whatever you wish.
;;
;;   Side view windows are special - they cannot take focus and we can
;;   operate on it only with mouse (!).  Some window operations like
;;   delete-other-windows (C-x 1) are slighty modified to treat side
;;   view windows specially.
;;
;; ** Bottom view window
;;
;;   Let auxiliary buffers (*shell*, *Messages*, *Help*, *Compilation*
;;   and another buffers with '*' in name) pop up/show in bottom
;;   window only.  BUT, if you want, you can open any buffer in any
;;   window (except side windows) as usual - that's only nice
;;   heuristic, not pressure.
;;
;;   Bottom view window remembers last selected buffer within it, so
;;   if you close this window and open later, it will show you buffer
;;   which you expect.
;;
;; ** Tabbars
;;
;;   Ide-skel uses (great) tabbar.el package with some modifications:
;;
;;     - there is no division into major mode groups (like in
;;       Eclipse),
;;
;;     - side view windows, bottom view window and editor windows have
;;       different tabsets,
;;
;;     - you can scroll tabs with mouse wheel,
;;
;;     - the Home button in window left corner acts as window menu
;;       (you can add your items to it in your plugin),
;;
;;     - mouse-3 click on tab kills its buffer
;;
;; * Project
;;
;;   Here, "project" means a directory tree checked out from CVS or
;;   SVN.  One project can contain source files of many types.  When
;;   we edit any project file, Emacs can easily find the project root
;;   directory simply by looking at filesystem.
;;
;;   So, we can execute many commands (grep, find, replace) on all
;;   project source files or on all project source files of the same
;;   type as file edited now (see Project menu).  Ide-skel package
;;   also automatically configures partial-completion-mode for project
;;   edited now.
;;
;;   There is no configuration for concrete projects needed (and
;;   that's great advantage in my opinion).

;; If you find this package useful, send me a postcard to address:
;;
;;  Peter Karpiuk
;;  Scott Tiger S.A.
;;  ul. Gawinskiego 8
;;  01-645 Warsaw
;;  Poland


;; * Notes for Emacs Lisp hackers
;;
;;   Each side window buffer should have:
;;
;;     - name that begins with space,
;;
;;     - tab label (string) - buffer local IDE-SKEL-TABBAR-TAB-LABEL
;;       variable,
;;
;;     - keep condition function (IDE-SKEL-KEEP-CONDITION-FUNCTION),
;;
;;     - menu (IDE-SKEL-TABBAR-MENU-FUNCTION) - optional.
;;
;;   Side window buffer is enabled (can be choosed by mouse click on
;;   his tab) if it has buffer local variable IDE-SKEL-TABBAR-ENABLED
;;   set to non-nil.  There may be many live side window buffers, but
;;   unavailable in current context ("context" means buffer edited in
;;   editor window) if they have IDE-SKEL-TABBAR-ENABLED set to nil.
;;
;;   Hiding side window operation disables all window buffers.  "Show
;;   side window" event handler should enable (and maybe create) side
;;   window buffers based on current context.  When you switch to
;;   other buffer in editor window (switching the context), all side
;;   window buffers for which keep condition function returns nil are
;;   disabled.  Handlers for EDITOR-BUFFER-CHANGED event should enable
;;   (and maybe create) additional buffers based on current context.
;;
;; ** Side window events
;;
;;   Event handlers should be implemented as an abnormal hook:
;;
;;     ide-skel-side-view-window-functions
;;
;;   It should be function with parameters
;;
;;     - side: symbol LEFT or RIGHT
;;
;;     - event-type: symbol for event:
;;       SHOW/EDITOR-BUFFER-CHANGED/TAB-CHANGE/HIDE
;;
;;     - list (optional): event parameters specific for event type.
;;
;;   Events are send only for opened (existing and visible) windows.
;;
;;   Hook functions are called in order until one of them returns
;;   non-nil.
;;
;; *** Show
;;
;;   After side window open.  Event handler should enable (and maybe
;;   create) buffers appropriate for current context.  After event
;;   handle, if no side window buffer is selected, there will be
;;   selected one of them.  No parameters.
;;
;; *** Editor Buffer Changed
;;
;;   After editor buffer changed (aka context switch).
;;
;;   Before event, buffers for which keep condition function returns
;;   nil, are disabled.  Event handler should enable (and maybe
;;   create) buffers appropriate for new context.
;;
;;   Parameters: before-buffer current-buffer.
;;
;; *** Tab Change
;;
;;   Before side window buffer change (as result of mouse click on tab
;;   or ide-skel-side-window-switch-to-buffer function call).
;;   Parameters: current-buffer new-buffer
;;
;; *** Hide
;;
;;   Before side window hiding.  After event handling, all side window
;;   buffers are disabled.
;;
;; *** Functions & vars
;;
;;   In plugins, you can use variables with self-descriptive names:
;;
;;   ide-skel-selected-frame
;;   ide-skel-current-editor-window
;;   ide-skel-current-editor-buffer
;;   ide-skel-current-left-view-window
;;   ide-skel-current-right-view-window
;;
;;   Moreover, when user selects another buffer to edit, the
;;
;;     ide-skel-editor-buffer-changed-hook
;;
;;   hook is run.  It is similar to "editor buffer changed" event, but
;;   has no parameters and is run even when all side windows are
;;   closed.
;;
;; **** Functions
;;
;;   ide-skel-side-window-switch-to-buffer (side-window buffer)
;;     Switch buffer in side window (please use only this function for
;;     this operation).
;;
;;   ide-skel-get-side-view-buffer-create (name side-sym tab-label
;;      help-string keep-condition-function)
;;     Create new buffer for side view window.  NAME should begin with
;;     space, side sym should be LEFT or RIGHT.
;;
;; **** Local variables in side window buffers
;;
;;   ide-skel-tabbar-tab-label
;;   ide-skel-tabbar-tab-help-string
;;   ide-skel-tabbar-menu-function
;;   ide-skel-tabbar-enabled
;;   ide-skel-keep-condition-function

(require 'cl)

;; Obsolete in emacs24
;(require 'complete)
(require 'tree-widget)
(require 'tabbar)
(require 'recentf)

(defgroup ide-skel nil
  "Ide Skeleton"
  :group 'tools
  :version 21)

(defcustom ide-skel-tabbar-hidden-buffer-names-regexp-list '("^TAGS" "^diary$")
  "Buffer name that matches any of this regexps, will have no tab."
  :group 'ide-skel
  :tag "Hidden Buffer Names Regexp List"
  :type '(repeat regexp)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (when tabbar-mode
	   (tabbar-init-tabsets-store))
	 (set-default symbol value)))

(defcustom ide-skel-bottom-view-buffer-names-regexps '("\\*.*\\*")
  "Buffers with names matched by one of this regexps will be shown in bottom view."
  :group 'ide-skel
  :tag "Bottom View Buffer Names Regexps"
  :type '(repeat regexp)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (when tabbar-mode
	   (tabbar-init-tabsets-store))
	 (set-default symbol value))
  )

(defcustom ide-skel-bottom-view-buffer-names-disallowed-regexps '("\\*info\\*" "\\*Backtrace\\*")
  "Buffers with names matched by one of this regexps will NOT be shown in bottom view."
  :group 'ide-skel
  :tag "Bottom View Buffer Names Disallowed Regexps"
  :type '(repeat regexp)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (when tabbar-mode
	   (tabbar-init-tabsets-store))
	 (set-default symbol value))
  )

(defconst ide-skel-left-view-window-tabset-name "LeftView")
(defconst ide-skel-right-view-window-tabset-name "RightView")
(defconst ide-skel-bottom-view-window-tabset-name "BottomView")
(defconst ide-skel-editor-window-tabset-name "Editor")

(defun ide-skel-shine-color (color percent)
  (when (equal color "unspecified-bg")
    (setq color (if (< percent 0) "white" "black")))
  (apply 'format "#%02x%02x%02x" 
         (mapcar (lambda (value)
                   (min 65535 (max 0 (* (+ (/ value 650) percent) 650))))
                 (color-values color))))

(defun ide-skel-color-percentage (color)
  (truncate (* (/ (/ (reduce '+ (color-values color)) 3.0) 65535.0) 100.0)))

(defun ide-skel-shine-face-background (face-sym percent)
  (when (>= (ide-skel-color-percentage (face-background 'default)) 50)
    (setq percent (- percent)))
  (set-face-attribute face-sym nil
		      :background (ide-skel-shine-color (face-background 'default) percent)))

(defun ide-skel-shine-face-foreground (face-sym percent)
  (when (>= (ide-skel-color-percentage (face-foreground 'default)) 50)
    (setq percent (- percent)))
  (set-face-attribute face-sym nil
		      :foreground (ide-skel-shine-color (face-foreground 'default) percent)))


(defvar ide-skel-tabbar-tab-label-max-width 25
  "Max width for tab label.  Nil means no limit.  If label width is too big, it will be shortened with ... inside.")

(defvar ide-skel-tabbar-tab-label nil
  "Tab name.  Local for buffer in side view window.")
(make-variable-buffer-local 'ide-skel-tabbar-tab-label)

(defvar ide-skel-tabbar-tab-help-string nil
  "Tooltip text for tab in side view window.  Buffer local.")
(make-variable-buffer-local 'ide-skel-tabbar-tab-help-string)

(defvar ide-skel-tabset-name nil)
(make-variable-buffer-local 'ide-skel-tabset-name)

(defvar ide-skel-tabbar-menu-function nil)
(make-variable-buffer-local 'ide-skel-tabbar-menu-function)

(defvar ide-skel-tabbar-enabled nil)
(make-variable-buffer-local 'ide-skel-tabbar-enabled)

(defvar ide-skel-keep-condition-function nil)
(make-variable-buffer-local 'ide-skel-keep-condition-function)

(defvar ide-skel-current-left-view-window nil)
(defvar ide-skel-current-right-view-window nil)
(defvar ide-skel-current-editor-window nil)
(defvar ide-skel-current-editor-buffer nil)
(defvar ide-skel-selected-frame nil)

(defconst ide-skel-left-view-window-xpm "\
/* XPM */
static char * left_view_xpm[] = {
\"24 24 145 2\",
\"  	c None\",
\". 	c #000000\",
\"+ 	c #FBFED6\",
\"@ 	c #F3F6CE\",
\"# 	c #EBEEC7\",
\"$ 	c #E3E7BF\",
\"% 	c #DCE0B9\",
\"& 	c #D5D9B2\",
\"* 	c #FFFFFF\",
\"= 	c #FDFDFD\",
\"- 	c #F9F9F9\",
\"; 	c #F4F4F4\",
\"> 	c #DDDDDD\",
\", 	c #F2F5CD\",
\"' 	c #E4E8C0\",
\") 	c #DDE1BA\",
\"! 	c #D7DAB4\",
\"~ 	c #D1D4AE\",
\"{ 	c #FEFEFE\",
\"] 	c #FBFBFB\",
\"^ 	c #F8F8F8\",
\"/ 	c #F5F5F5\",
\"( 	c #F2F2F2\",
\"_ 	c #DBDBDB\",
\": 	c #E9EDC5\",
\"< 	c #D8DBB5\",
\"[ 	c #D2D5AF\",
\"} 	c #CDD0AA\",
\"| 	c #FCFCFC\",
\"1 	c #F6F6F6\",
\"2 	c #F3F3F3\",
\"3 	c #F0F0F0\",
\"4 	c #DADADA\",
\"5 	c #E1E5BD\",
\"6 	c #CDD0AB\",
\"7 	c #C8CCA6\",
\"8 	c #FAFAFA\",
\"9 	c #F7F7F7\",
\"0 	c #EFEFEF\",
\"a 	c #D9D9D9\",
\"b 	c #DADDB6\",
\"c 	c #C4C7A2\",
\"d 	c #EDEDED\",
\"e 	c #D7D7D7\",
\"f 	c #D3D6B0\",
\"g 	c #CFD3AD\",
\"h 	c #CBCFA9\",
\"i 	c #C8CBA6\",
\"j 	c #C0C39F\",
\"k 	c #F1F1F1\",
\"l 	c #EEEEEE\",
\"m 	c #ECECEC\",
\"n 	c #D6D6D6\",
\"o 	c #C9CDA7\",
\"p 	c #C6C9A4\",
\"q 	c #C3C6A1\",
\"r 	c #BFC39E\",
\"s 	c #BCBF9B\",
\"t 	c #EAEAEA\",
\"u 	c #D4D4D4\",
\"v 	c #C7CAA5\",
\"w 	c #C1C5A0\",
\"x 	c #BEC29D\",
\"y 	c #BBBF9B\",
\"z 	c #B9BC98\",
\"A 	c #EBEBEB\",
\"B 	c #E8E8E8\",
\"C 	c #D3D3D3\",
\"D 	c #C2C5A0\",
\"E 	c #BDC09C\",
\"F 	c #BABE99\",
\"G 	c #B8BB97\",
\"H 	c #B5B895\",
\"I 	c #E9E9E9\",
\"J 	c #E7E7E7\",
\"K 	c #D1D1D1\",
\"L 	c #BBBE9A\",
\"M 	c #B7BA96\",
\"N 	c #B4B794\",
\"O 	c #B2B592\",
\"P 	c #E5E5E5\",
\"Q 	c #D0D0D0\",
\"R 	c #B3B693\",
\"S 	c #B1B491\",
\"T 	c #AFB28F\",
\"U 	c #E3E3E3\",
\"V 	c #CECECE\",
\"W 	c #B4B793\",
\"X 	c #B0B390\",
\"Y 	c #AEB18F\",
\"Z 	c #ACAF8D\",
\"` 	c #E6E6E6\",
\" .	c #E4E4E4\",
\"..	c #E2E2E2\",
\"+.	c #CDCDCD\",
\"@.	c #ADB08E\",
\"#.	c #ABAE8C\",
\"$.	c #AAAD8B\",
\"%.	c #E0E0E0\",
\"&.	c #CBCBCB\",
\"*.	c #A9AC8A\",
\"=.	c #A7AA89\",
\"-.	c #DEDEDE\",
\";.	c #CACACA\",
\">.	c #ABAE8B\",
\",.	c #A8AB89\",
\"'.	c #A6A988\",
\").	c #A5A887\",
\"!.	c #C8C8C8\",
\"~.	c #A7AA88\",
\"{.	c #A6A987\",
\"].	c #A4A786\",
\"^.	c #A3A685\",
\"/.	c #DFDFDF\",
\"(.	c #C7C7C7\",
\"_.	c #A5A886\",
\":.	c #A2A584\",
\"<.	c #A1A483\",
\"[.	c #C6C6C6\",
\"}.	c #A4A785\",
\"|.	c #A0A382\",
\"1.	c #9FA282\",
\"2.	c #D8D8D8\",
\"3.	c #C4C4C4\",
\"4.	c #A3A684\",
\"5.	c #A2A484\",
\"6.	c #A0A383\",
\"7.	c #9EA181\",
\"8.	c #9DA080\",
\"9.	c #C3C3C3\",
\"0.	c #8D8F72\",
\"a.	c #8C8E72\",
\"b.	c #8B8D71\",
\"c.	c #8A8C70\",
\"d.	c #898B6F\",
\"e.	c #888A6F\",
\"f.	c #C5C5C5\",
\"g.	c #C2C2C2\",
\"h.	c #C1C1C1\",
\"i.	c #C0C0C0\",
\"j.	c #BEBEBE\",
\"k.	c #BDBDBD\",
\"l.	c #BBBBBB\",
\"m.	c #BABABA\",
\"n.	c #ABABAB\",
\"                                                \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\". + @ # $ % & . * * * * * * * * * * = - ; ; > . \",
\". , # ' ) ! ~ . * * * * * * * * * { ] ^ / ( _ . \",
\". : $ ) < [ } . * * * * * * * * * | - 1 2 3 4 . \",
\". 5 % ! [ 6 7 . * * * * * * * * = 8 9 ; 3 0 a . \",
\". b & ~ } 7 c . * * * * * * * { ] ^ / ( 0 d e . \",
\". f g h i c j . * * * * * * * | - 1 2 k l m n . \",
\". } o p q r s . * * * * * * = 8 9 ; 3 0 m t u . \",
\". v c w x y z . * * * * * = 8 9 / ( 0 d A B C . \",
\". D r E F G H . * * * * { ] ^ / 2 3 l A I J K . \",
\". E L z M N O . * * * { ] ^ 1 2 3 l m I J P Q . \",
\". z M H R S T . * * { ] ^ 1 2 k l m t B P U V . \",
\". H W O X Y Z . * = ] ^ 1 2 k 0 m t B `  ...+.. \",
\". O X T @.#.$.. = 8 ^ 1 2 k 0 m t B `  ...%.&.. \",
\". T @.Z $.*.=.. 8 9 / 2 k 0 m t B `  ...%.-.;.. \",
\". Z >.*.,.'.).. 9 / 2 3 l m t B `  ...%.-.> !.. \",
\". *.,.~.{.].^.. ; ( 3 l m t B `  ...%./.> _ (.. \",
\". ~.{._.^.:.<.. k 0 l m t B `  ...%./.> _ a [.. \",
\". _.}.:.<.|.1.. 0 d A I B `  ...%./.> _ a 2.3.. \",
\". 4.5.6.1.7.8.. m A I J P  ...%.-.> _ a 2.n 9.. \",
\". 0.a.b.c.d.e.. +.&.;.!.(.f.3.g.h.i.j.k.l.m.n.. \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\"                                                \"};
"
  "XPM format image used as left view window icon")

(defconst ide-skel-left-view-window-image
  (create-image ide-skel-left-view-window-xpm 'xpm t))

(defconst ide-skel-right-view-window-xpm "\
/* XPM */
static char * right_view_xpm[] = {
\"24 24 125 2\",
\"  	c None\",
\". 	c #000000\",
\"+ 	c #FFFFFF\",
\"@ 	c #A8AB89\",
\"# 	c #A6A987\",
\"$ 	c #A4A785\",
\"% 	c #A2A484\",
\"& 	c #A0A282\",
\"* 	c #919376\",
\"= 	c #A7AA88\",
\"- 	c #A5A886\",
\"; 	c #A2A584\",
\"> 	c #A0A383\",
\", 	c #9FA181\",
\"' 	c #909275\",
\") 	c #A3A685\",
\"! 	c #A1A483\",
\"~ 	c #9FA282\",
\"{ 	c #9DA080\",
\"] 	c #8F9174\",
\"^ 	c #A4A786\",
\"/ 	c #A0A382\",
\"( 	c #9EA181\",
\"_ 	c #9C9F7F\",
\": 	c #8E9073\",
\"< 	c #FEFEFE\",
\"[ 	c #9B9E7F\",
\"} 	c #8D8F73\",
\"| 	c #FCFCFC\",
\"1 	c #A1A484\",
\"2 	c #9EA180\",
\"3 	c #9A9D7E\",
\"4 	c #8C8E72\",
\"5 	c #FDFDFD\",
\"6 	c #FAFAFA\",
\"7 	c #9B9E7E\",
\"8 	c #999C7D\",
\"9 	c #8B8D71\",
\"0 	c #F7F7F7\",
\"a 	c #9FA281\",
\"b 	c #9A9C7D\",
\"c 	c #989B7C\",
\"d 	c #8A8C70\",
\"e 	c #FBFBFB\",
\"f 	c #F8F8F8\",
\"g 	c #F5F5F5\",
\"h 	c #9C9E7F\",
\"i 	c #9A9D7D\",
\"j 	c #979A7B\",
\"k 	c #898B70\",
\"l 	c #F6F6F6\",
\"m 	c #F3F3F3\",
\"n 	c #999C7C\",
\"o 	c #96997A\",
\"p 	c #888A6F\",
\"q 	c #F1F1F1\",
\"r 	c #9B9D7E\",
\"s 	c #989A7B\",
\"t 	c #959779\",
\"u 	c #87896E\",
\"v 	c #EFEFEF\",
\"w 	c #959879\",
\"x 	c #949678\",
\"y 	c #86886D\",
\"z 	c #ECECEC\",
\"A 	c #97997B\",
\"B 	c #949778\",
\"C 	c #939577\",
\"D 	c #85876C\",
\"E 	c #EAEAEA\",
\"F 	c #95987A\",
\"G 	c #919476\",
\"H 	c #84876C\",
\"I 	c #F9F9F9\",
\"J 	c #F0F0F0\",
\"K 	c #EEEEEE\",
\"L 	c #E8E8E8\",
\"M 	c #949779\",
\"N 	c #939578\",
\"O 	c #929476\",
\"P 	c #909375\",
\"Q 	c #83866B\",
\"R 	c #F4F4F4\",
\"S 	c #F2F2F2\",
\"T 	c #E6E6E6\",
\"U 	c #939678\",
\"V 	c #929477\",
\"W 	c #909376\",
\"X 	c #8F9275\",
\"Y 	c #82856A\",
\"Z 	c #E4E4E4\",
\"` 	c #8E9174\",
\" .	c #818469\",
\"..	c #EDEDED\",
\"+.	c #EBEBEB\",
\"@.	c #E9E9E9\",
\"#.	c #E2E2E2\",
\"$.	c #8D9073\",
\"%.	c #808368\",
\"&.	c #E7E7E7\",
\"*.	c #E5E5E5\",
\"=.	c #E0E0E0\",
\"-.	c #8C8F72\",
\";.	c #7F8268\",
\">.	c #D6D6D6\",
\",.	c #D5D5D5\",
\"'.	c #D4D4D4\",
\").	c #D2D2D2\",
\"!.	c #D1D1D1\",
\"~.	c #D0D0D0\",
\"{.	c #CECECE\",
\"].	c #CDCDCD\",
\"^.	c #CBCBCB\",
\"/.	c #CACACA\",
\"(.	c #C8C8C8\",
\"_.	c #C7C7C7\",
\":.	c #C5C5C5\",
\"<.	c #C4C4C4\",
\"[.	c #C2C2C2\",
\"}.	c #7D8066\",
\"|.	c #7C7F65\",
\"1.	c #7B7E64\",
\"2.	c #7B7D64\",
\"3.	c #7A7C63\",
\"4.	c #70725B\",
\"                                                \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\". + + + + + + + + + + + + + + + . @ # $ % & * . \",
\". + + + + + + + + + + + + + + + . = - ; > , ' . \",
\". + + + + + + + + + + + + + + + . # ) ! ~ { ] . \",
\". + + + + + + + + + + + + + + + . ^ ; / ( _ : . \",
\". + + + + + + + + + + + + + + < . ) ! ~ { [ } . \",
\". + + + + + + + + + + + + + + | . 1 & 2 _ 3 4 . \",
\". + + + + + + + + + + + + + 5 6 . > ( _ 7 8 9 . \",
\". + + + + + + + + + + + + 5 6 0 . a { 7 b c d . \",
\". + + + + + + + + + + + < e f g . { h i c j k . \",
\". + + + + + + + + + + < e f l m . _ 3 n j o p . \",
\". + + + + + + + + + < e f l m q . r 8 s o t u . \",
\". + + + + + + + + 5 e f l m q v . 8 c o w x y . \",
\". + + + + + + + 5 6 f l m q v z . c A w B C D . \",
\". + + + + + < | 6 0 g m q v z E . A F B C G H . \",
\". + + + + 5 e I 0 g m J K z E L . F M N O P Q . \",
\". + + < | 6 f l R S J K z E L T . M U V W X Y . \",
\". < 5 e I 0 g m q v K z E L T Z . U V * X `  .. \",
\". e I f l R S q v ..+.@.L T Z #.. V * X ` $.%.. \",
\". f l g m q J K z +.@.&.*.Z #.=.. W X ` $.-.;.. \",
\". >.,.'.).!.~.{.].^./.(._.:.<.[.. }.|.1.2.3.4.. \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\"                                                \"};
"
  "XPM format image used as right view window icon")

(defconst ide-skel-right-view-window-image
  (create-image ide-skel-right-view-window-xpm 'xpm t))

(defconst ide-skel-bottom-view-window-xpm "\
/* XPM */
static char * bottom_view_xpm[] = {
\"24 24 130 2\",
\"  	c None\",
\". 	c #000000\",
\"+ 	c #FFFFFF\",
\"@ 	c #FDFDFD\",
\"# 	c #F9F9F9\",
\"$ 	c #F6F6F6\",
\"% 	c #F4F4F4\",
\"& 	c #DDDDDD\",
\"* 	c #FEFEFE\",
\"= 	c #FBFBFB\",
\"- 	c #F8F8F8\",
\"; 	c #F5F5F5\",
\"> 	c #F2F2F2\",
\", 	c #DBDBDB\",
\"' 	c #FCFCFC\",
\") 	c #F3F3F3\",
\"! 	c #F0F0F0\",
\"~ 	c #DADADA\",
\"{ 	c #FAFAFA\",
\"] 	c #F7F7F7\",
\"^ 	c #F1F1F1\",
\"/ 	c #EFEFEF\",
\"( 	c #D9D9D9\",
\"_ 	c #EDEDED\",
\": 	c #D7D7D7\",
\"< 	c #EEEEEE\",
\"[ 	c #ECECEC\",
\"} 	c #D6D6D6\",
\"| 	c #EAEAEA\",
\"1 	c #D4D4D4\",
\"2 	c #EBEBEB\",
\"3 	c #E8E8E8\",
\"4 	c #D3D3D3\",
\"5 	c #E9E9E9\",
\"6 	c #E7E7E7\",
\"7 	c #D1D1D1\",
\"8 	c #E5E5E5\",
\"9 	c #D0D0D0\",
\"0 	c #E3E3E3\",
\"a 	c #CECECE\",
\"b 	c #E6E6E6\",
\"c 	c #E4E4E4\",
\"d 	c #E2E2E2\",
\"e 	c #CDCDCD\",
\"f 	c #E0E0E0\",
\"g 	c #CBCBCB\",
\"h 	c #CCCFAB\",
\"i 	c #CACDAA\",
\"j 	c #C8CBA8\",
\"k 	c #C7CAA7\",
\"l 	c #C5C8A5\",
\"m 	c #C3C6A4\",
\"n 	c #C2C5A3\",
\"o 	c #C0C3A1\",
\"p 	c #BEC1A0\",
\"q 	c #BDBF9E\",
\"r 	c #BBBE9D\",
\"s 	c #B9BC9B\",
\"t 	c #B8BA9A\",
\"u 	c #B6B999\",
\"v 	c #B4B797\",
\"w 	c #B3B596\",
\"x 	c #B1B495\",
\"y 	c #B0B293\",
\"z 	c #AEB192\",
\"A 	c #ADAF91\",
\"B 	c #ABAE8F\",
\"C 	c #9C9E82\",
\"D 	c #C9CCA8\",
\"E 	c #C6C9A6\",
\"F 	c #C4C7A5\",
\"G 	c #C1C4A2\",
\"H 	c #BFC2A1\",
\"I 	c #BEC19F\",
\"J 	c #BCBF9E\",
\"K 	c #BABD9C\",
\"L 	c #B7BA9A\",
\"M 	c #B6B998\",
\"N 	c #ABAE90\",
\"O 	c #AAAD8E\",
\"P 	c #9A9D81\",
\"Q 	c #C2C4A2\",
\"R 	c #BFC1A0\",
\"S 	c #BDC09F\",
\"T 	c #BCBE9D\",
\"U 	c #B9BB9B\",
\"V 	c #B7BA99\",
\"W 	c #B6B898\",
\"X 	c #B1B494\",
\"Y 	c #A9AB8D\",
\"Z 	c #999C80\",
\"` 	c #C1C3A2\",
\" .	c #BFC2A0\",
\"..	c #B9BC9C\",
\"+.	c #B8BB9A\",
\"@.	c #B7B999\",
\"#.	c #B5B898\",
\"$.	c #B4B697\",
\"%.	c #B2B596\",
\"&.	c #AAAD8F\",
\"*.	c #A7AA8C\",
\"=.	c #989B80\",
\"-.	c #BDC09E\",
\";.	c #B3B696\",
\">.	c #B2B595\",
\",.	c #B1B394\",
\"'.	c #AFB293\",
\").	c #A6A98B\",
\"!.	c #97997F\",
\"~.	c #A7A98C\",
\"{.	c #A6A88B\",
\"].	c #A4A78A\",
\"^.	c #A3A689\",
\"/.	c #A2A588\",
\"(.	c #A1A487\",
\"_.	c #A0A286\",
\":.	c #9FA185\",
\"<.	c #9EA084\",
\"[.	c #9D9F83\",
\"}.	c #9B9E82\",
\"|.	c #999B80\",
\"1.	c #989A7F\",
\"2.	c #97997E\",
\"3.	c #96987D\",
\"4.	c #95977D\",
\"5.	c #94967C\",
\"6.	c #92957B\",
\"7.	c #91947A\",
\"8.	c #909279\",
\"9.	c #85876F\",
\"                                                \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\". + + + + + + + + + + + + + + + + + @ # $ % & . \",
\". + + + + + + + + + + + + + + + + * = - ; > , . \",
\". + + + + + + + + + + + + + + + + ' # $ ) ! ~ . \",
\". + + + + + + + + + + + + + + + @ { ] % ^ / ( . \",
\". + + + + + + + + + + + + + + * = - ; > ! _ : . \",
\". + + + + + + + + + + + + + + ' # $ ) / < [ } . \",
\". + + + + + + + + + + + + + @ { ] % ^ < [ | 1 . \",
\". + + + + + + + + + + + + @ { ] ; > / _ 2 3 4 . \",
\". + + + + + + + + + + + * = - ; > ! < 2 5 6 7 . \",
\". + + + + + + + + + + * = - $ ) ! < [ 5 6 8 9 . \",
\". + + + + + + + + + * = - $ ) ^ < [ | 3 8 0 a . \",
\". + + + + + + + + @ = - $ ) ^ / [ | 3 b c d e . \",
\". + + + + + + + @ { - $ ) ^ / [ | 3 b c d f g . \",
\". . . . . . . . . . . . . . . . . . . . . . . . \",
\". h i j k l m n o p q r s t u v w x y z A B C . \",
\". D k E F n G H I J K s L M v w x y z A N O P . \",
\". E F m Q o R S T K U V W v w X y z A N O Y Z . \",
\". m n `  .I J r ..+.@.#.$.%.X y z A N &.Y *.=.. \",
\". G H p -.T K s t u #.;.>.,.'.z A N O Y *.).!.. \",
\". ~.{.].^./.(._.:.<.[.}.P |.1.2.3.4.5.6.7.8.9.. \",
\"  . . . . . . . . . . . . . . . . . . . . . .   \",
\"                                                \"};
"
  "XPM format image used as bottom view window icon")

(defconst ide-skel-bottom-view-window-image
  (create-image ide-skel-bottom-view-window-xpm 'xpm t))

(defvar ide-skel-win--win2-switch t)

(defvar ide-skel-win--minibuffer-selected-p nil)

;; (copy-win-node w)
;; (win-node-corner-pos w)
;; (make-win-node :corner-pos 0 :buffer b :horiz-scroll 0 :point 0 :mark nil :divisions nil)
;; (win-node-p w)
(defstruct win-node
  "Window configuration tree node."
  (corner-pos nil)   ; pair - original position of left top window corner
  (buf-corner-pos 1) ; position within the buffer at the upper left of the window
  buffer             ; the buffer window displays
  (horiz-scroll 0)   ; amount of horizontal scrolling, in columns
  (point 1)          ; point
  (mark nil)         ; the mark
  (edges nil)        ; (window-edges)
  (cursor-priority nil) 
  (fixed-size nil)
  (divisions nil))   ; children (list of division)

(defstruct division
  "Podzial okienka"
  win-node      ; winnode for window after division
  horizontal-p  ; division horizontal or vertical
  percent)      ; 0.0-1.0: width/height of parent after division

(defvar sel-window nil)
(defvar sel-priority nil)

(defvar ide-skel-ommited-windows nil)

(defvar ide-skel--fixed-size-windows nil)

;; args: 'left/right 'show/editor-buffer-changed/hide/tab-change &rest buffer...
(defvar ide-skel-side-view-window-functions nil)

(defvar ide-skel-editor-buffer-changed-hook nil)

(defvar ide-skel-last-buffer-change-event nil)
(defvar ide-skel-last-selected-window-or-buffer nil)

(defcustom ide-skel-bottom-view-window-size 0.35
  "Default bottom view window height in characters (int >= 5) or percent of Emacs frame height (0.0 - 1.0)"
  :group 'ide-skel
  :tag "Default Bottom View Window Height"
  :type (list 'restricted-sexp
	      :match-alternatives (list (lambda (value)
					  (or (and (floatp value)
						   (> value 0.0)
						   (< value 1.0))
					      (and (integerp value)
						   (>= value 5)))))))

(defcustom ide-skel-bottom-view-on-left-view t
  "Non-nil if bottom view lies partially on left view."
  :group 'ide-skel
  :tag "Bottom View on Left View"
  :type '(boolean)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (let ((is-bottom-view-window (ide-skel-get-bottom-view-window)))
	   (when is-bottom-view-window
	     (ide-skel-hide-bottom-view-window))
	   (unwind-protect
	       (set-default symbol value)
	     (when is-bottom-view-window
	       (ide-skel-show-bottom-view-window))))))

(defcustom ide-skel-bottom-view-on-right-view t
  "Non-nil if bottom view lies partially on right view."
  :group 'ide-skel
  :tag "Bottom View on Right View"
  :type '(boolean)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (let ((is-bottom-view-window (ide-skel-get-bottom-view-window)))
	   (when is-bottom-view-window
	     (ide-skel-hide-bottom-view-window))
	   (unwind-protect
	       (set-default symbol value)
	     (when is-bottom-view-window
	       (ide-skel-show-bottom-view-window))))))

(defconst ide-skel-unexpected-bottom-view-window-buffer-names '("*Completions*" "*Compile-Log*"))

(defvar ide-skel--last-bottom-view-buffer-name nil)

(defvar ide-skel-was-scratch nil)

(defvar ide-skel-bottom-view-window-oper-in-progress nil)

(defvar ide-skel--current-side-windows (cons nil nil))

(defcustom ide-skel-left-view-window-width 25
  "Default width of left view window."
  :group 'ide-skel
  :tag "Default Left View Window Width"
  :type '(integer)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (let ((is-left-view-window (ide-skel-get-left-view-window)))
	   (when is-left-view-window
	     (ide-skel-hide-left-view-window))
	   (unwind-protect
	       (set-default symbol value)
	     (when is-left-view-window
	       (ide-skel-show-left-view-window))))))

(defcustom ide-skel-right-view-window-width 30
  "Default width of right view window."
  :group 'ide-skel
  :tag "Default Right View Window Width"
  :type '(integer)
  :initialize 'custom-initialize-default
  :set (lambda (symbol value)
	 (let ((is-right-view-window (ide-skel-get-right-view-window)))
	   (when is-right-view-window
	     (ide-skel-hide-right-view-window))
	   (unwind-protect
	       (set-default symbol value)
	     (when is-right-view-window
	       (ide-skel-show-right-view-window))))))

(defcustom ide-skel-side-view-display-cursor nil
  "Non-nil if cursor should be displayed in side view windows"
  :group 'ide-skel
  :tag "Side View Display Cursor"
  :type 'boolean)

(defvar ide-skel-highlight-face 'ide-skel-highlight-face)
(defface ide-skel-highlight-face
  (list 
   (list '((background light))
         (append (list :inherit 'variable-pitch :background (ide-skel-shine-color (face-background 'default) -70) :foreground (face-background 'default))
                 (when (>= emacs-major-version 22) '(:box (:style released-button)))))
   (list '((background dark))
         (append (list :inherit 'variable-pitch :background (ide-skel-shine-color (face-background 'default) +70) :foreground (face-background 'default))
                 (when (>= emacs-major-version 22) '(:box (:style released-button)))))
   '(t (:inherit default)))
  "Face for selection in side views."
  :group 'ide-skel)

;;; buffer -> alist
;;;   :imenu-buffer
;;;   :default-left-tab-label, :default-right-tab-label
(defvar ide-skel-context-properties (make-hash-table :test 'eq))

(defvar ide-skel-last-left-view-window-tab-label nil)
(defvar ide-skel-last-right-view-window-tab-label nil)

(defvar ide-skel-buffer-list-buffer nil)
(defvar ide-skel-buffer-list nil)

(defvar ide-skel-buffer-list-tick nil)

(defconst ide-skel-tree-widget-open-xpm "\
/* XPM */
static char *open[] = {
/* columns rows colors chars-per-pixel */
\"11 15 49 1\",
\"  c #4D084D080B7B\",
\". c #5A705A700DBB\",
\"X c #7B647B6404B5\",
\"o c #7818781810F1\",
\"O c #7E1E7E1E16D4\",
\"+ c #5EB75D2D6FCF\",
\"@ c #5FD85D2D6FCF\",
\"# c #60415D2D6FCF\",
\"$ c #88BD88BD068F\",
\"% c #8A5D8A5D0969\",
\"& c #82F782F71033\",
\"* c #841B841B1157\",
\"= c #87BC87BC1125\",
\"- c #878787871696\",
\"; c #87D587BE172E\",
\": c #87C187C11812\",
\"> c #895A895A1B9C\",
\", c #8A0A8A0A1C10\",
\"< c #8E5B8DF21DE7\",
\"1 c #95DF95DF1A5F\",
\"2 c #95CC95CC1B5B\",
\"3 c #98D498D41EE5\",
\"4 c #9BBB9BBB2414\",
\"5 c #9BBB9BBB2622\",
\"6 c #9CDF9CDF2696\",
\"7 c #984C984C281C\",
\"8 c #9EA19EA129C1\",
\"9 c #A060A0602B4B\",
\"0 c #A3BAA3BA3148\",
\"q c #A78AA78A36FD\",
\"w c #A7BBA7BB38D9\",
\"e c #A7B7A7B73B03\",
\"r c #AB1AAB1A3B03\",
\"t c #ABD7ABD73C6C\",
\"y c #AFC5AFC54435\",
\"u c #B5D2B5D24A67\",
\"i c #B659B6594AEE\",
\"p c #B959B9595378\",
\"a c #BBCEBBCE5267\",
\"s c #BE64BE645A53\",
\"d c #C2D2C2D26078\",
\"f c #C43BC43B60D8\",
\"g c #C42EC42E60EE\",
\"h c #C44FC44F60EC\",
\"j c #C73BC73B66E7\",
\"k c #C65DC65D697B\",
\"l c #CECECECE7676\",
\"z c #D02CD02C7B7B\",
\"x c None\",
/* pixels */
\"xxxxxxxxxxx\",
\"xxxxxxxxxxx\",
\"xxxxxxxxxxx\",
\"xxxxxxxxxxx\",
\"x,> xxxxxxx\",
\"6zlpw07xxxx\",
\"5k32211=oxx\",
\"49ryuasfexx\",
\"$8yuasgdOxx\",
\"%qiashjtxxx\",
\"X&*<;-:.xxx\",
\"xxx@xxxxxxx\",
\"xxx#xxxxxxx\",
\"xxx+xxxxxxx\",
\"xxx+xxxxxxx\"
};
")

(defconst ide-skel-tree-widget-open-image
  (create-image ide-skel-tree-widget-open-xpm 'xpm t))

(defconst ide-skel-tree-widget-no-handle-xpm "\
/* XPM */
static char *no_handle[] = {
/* columns rows colors chars-per-pixel */
\"7 15 1 1\",
\"  c None\",
/* pixels */
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \",
\"       \"
};
")

(defconst ide-skel-tree-widget-no-handle-image
  (create-image ide-skel-tree-widget-no-handle-xpm 'xpm t))

(defconst ide-skel-tree-widget-no-guide-xpm "\
/* XPM */
static char *no_guide[] = {
/* columns rows colors chars-per-pixel */
\"4 15 1 1\",
\"  c None\",
/* pixels */
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \",
\"    \"
};
")

(defconst ide-skel-tree-widget-no-guide-image
  (create-image ide-skel-tree-widget-no-guide-xpm 'xpm t))

(defconst ide-skel-tree-widget-leaf-xpm "\
/* XPM */
static char *leaf[] = {
/* columns rows colors chars-per-pixel */
\"11 15 42 1\",
\"  c #224222422242\",
\". c #254525452545\",
\"X c #272727272727\",
\"o c #31DA31DA31DA\",
\"O c #4CAC4CAC4CAC\",
\"+ c #4F064F064F06\",
\"@ c #506050605060\",
\"# c #511651165116\",
\"$ c #57D657D657D6\",
\"% c #59A559A559A5\",
\"& c #5AAC5AAC5AAC\",
\"* c #5D5A5D5A5D5A\",
\"= c #5F025F025F02\",
\"- c #60C660C660C6\",
\"; c #617D617D617D\",
\": c #63D363D363D3\",
\"> c #8B908B908B90\",
\", c #8E3C8E3C8E3C\",
\"< c #8F588F588F58\",
\"1 c #93FC93FC93FC\",
\"2 c #949194919491\",
\"3 c #96AD96AD96AD\",
\"4 c #991899189918\",
\"5 c #99EA99EA99EA\",
\"6 c #9B619B619B61\",
\"7 c #9CD69CD69CD6\",
\"8 c #9E769E769E76\",
\"9 c #9FA59FA59FA5\",
\"0 c #A0C3A0C3A0C3\",
\"q c #A293A293A293\",
\"w c #A32EA32EA32E\",
\"e c #A480A480A480\",
\"r c #A5A5A5A5A5A5\",
\"t c #A755A755A755\",
\"y c #AA39AA39AA39\",
\"u c #AC77AC77AC77\",
\"i c #B1B7B1B7B1B7\",
\"p c #B283B283B283\",
\"a c #B7B7B7B7B7B7\",
\"s c #BD02BD02BD02\",
\"d c gray74\",
\"f c None\",
/* pixels */
\"fffffffffff\",
\"fffffffffff\",
\"fffffffffff\",
\"XXXXfffffff\",
\"%,25#offfff\",
\"*6qr$&.ffff\",
\"=1<3>wOffff\",
\";6648a@ffff\",
\";wweys#ffff\",
\":970ed#ffff\",
\"-tuipp+ffff\",
\"XXXXXX ffff\",
\"fffffffffff\",
\"fffffffffff\",
\"fffffffffff\"
};
")

(defconst ide-skel-tree-widget-leaf-image
  (create-image ide-skel-tree-widget-leaf-xpm 'xpm t))

(defconst ide-skel-tree-widget-handle-xpm "\
/* XPM */
static char *handle[] = {
/* columns rows colors chars-per-pixel */
\"7 15 2 1\",
\"  c #56D752D36363\",
\". c None\",
/* pixels */
\".......\",
\".......\",
\".......\",
\".......\",
\".......\",
\".......\",
\".......\",
\"       \",
\".......\",
\".......\",
\".......\",
\".......\",
\".......\",
\".......\",
\".......\"
};
")

(defconst ide-skel-tree-widget-handle-image
  (create-image ide-skel-tree-widget-handle-xpm 'xpm t))

(defconst ide-skel-tree-widget-guide-xpm "\
/* XPM */
static char *guide[] = {
/* columns rows colors chars-per-pixel */
\"4 15 2 1\",
\"  c #73C96E6E8484\",
\". c None\",
/* pixels */
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \"
};
")

(defconst ide-skel-tree-widget-guide-image
  (create-image ide-skel-tree-widget-guide-xpm 'xpm t))

(defconst ide-skel-tree-widget-end-guide-xpm "\
/* XPM */
static char *end_guide[] = {
/* columns rows colors chars-per-pixel */
\"4 15 2 1\",
\"  c #73C96E6E8484\",
\". c None\",
/* pixels */
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"... \",
\"....\",
\"....\",
\"....\",
\"....\",
\"....\",
\"....\",
\"....\"
};
")

(defconst ide-skel-tree-widget-end-guide-image
  (create-image ide-skel-tree-widget-end-guide-xpm 'xpm t))

(defconst ide-skel-tree-widget-empty-xpm "\
/* XPM */
static char *empty[] = {
/* columns rows colors chars-per-pixel */
\"11 15 39 1\",
\"  c #2BCF2BCF2BCF\",
\". c #31F831F831F8\",
\"X c #3F283F283F28\",
\"o c #41B141B141B1\",
\"O c #467946794679\",
\"+ c #476747674767\",
\"@ c #484648464846\",
\"# c #498749874987\",
\"$ c #4B684B684B68\",
\"% c #524F524F524F\",
\"& c #52D352D352D3\",
\"* c #554155415541\",
\"= c #561C561C561C\",
\"- c #598659865986\",
\"; c #5D775D775D77\",
\": c #5E7E5E7E5E7E\",
\"> c #60CE60CE60CE\",
\", c #615161516151\",
\"< c #61F361F361F3\",
\"1 c #642464246424\",
\"2 c #654865486548\",
\"3 c #678767876787\",
\"4 c #68D868D868D8\",
\"5 c #699569956995\",
\"6 c #6D556D556D55\",
\"7 c #6FB56FB56FB5\",
\"8 c #72CF72CF72CF\",
\"9 c #731073107310\",
\"0 c #757775777577\",
\"q c #7B747B747B74\",
\"w c #809080908090\",
\"e c #81F281F281F2\",
\"r c #820D820D820D\",
\"t c #84F984F984F9\",
\"y c #858285828582\",
\"u c #95E295E295E2\",
\"i c #9FFF9FFF9FFF\",
\"p c #A5A5A5A5A5A5\",
\"a c None\",
/* pixels */
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\",
\"a&% aaaaaaa\",
\",piy76<aaaa\",
\">u-===*#oaa\",
\":14690qe3aa\",
\"+;680qewOaa\",
\"@290qrt5aaa\",
\"XO+@#$$.aaa\",
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\",
\"aaaaaaaaaaa\"
};
")

(defconst ide-skel-tree-widget-empty-image
  (create-image ide-skel-tree-widget-empty-xpm 'xpm t))

(defconst ide-skel-tree-widget-close-xpm "\
/* XPM */
static char *close[] = {
/* columns rows colors chars-per-pixel */
\"11 15 45 1\",
\"  c #4EA14EA10DFA\",
\". c #5AA05AA00C52\",
\"X c #75297529068F\",
\"o c #7B647B6404B5\",
\"O c #8B888B880B91\",
\"+ c #8EDE8EDE0F5F\",
\"@ c #82F782F71033\",
\"# c #83A683A61157\",
\"$ c #84AD84AD13BC\",
\"% c #857985791489\",
\"& c #868086801590\",
\"* c #8A8A8A8A1697\",
\"= c #878787871812\",
\"- c #885388531936\",
\"; c #8BAB8BAB17B8\",
\": c #8CCC8CCC1A7D\",
\"> c #8DB68DB61BC4\",
\", c #90EC90EC11D0\",
\"< c #9161916114B5\",
\"1 c #92A292A2163F\",
\"2 c #8E8B8E8B2150\",
\"3 c #8F0F8F0F2274\",
\"4 c #9AF79AF72386\",
\"5 c #9D289D282655\",
\"6 c #9ED19ED1286E\",
\"7 c #9F599F592912\",
\"8 c #A31DA31D2D82\",
\"9 c #A3DDA3DD2DA2\",
\"0 c #A144A1442ED2\",
\"q c #A828A82833B4\",
\"w c #AB38AB383AEB\",
\"e c #AD21AD213DC2\",
\"r c #AD6DAD6D3E56\",
\"t c #AFFCAFFC4481\",
\"y c #B0AAB0AA429F\",
\"u c #B1B1B1B144E8\",
\"i c #B51DB51D4A5F\",
\"p c #B535B5354A8A\",
\"a c #B56FB56F4AEE\",
\"s c #B7B0B7B0525B\",
\"d c #BD14BD1459B1\",
\"f c #BFACBFAC5C55\",
\"g c #C5D9C5D965F7\",
\"h c #C85FC85F6D04\",
\"j c None\",
/* pixels */
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\",
\"j32 jjjjjjj\",
\"1uy84570.jj\",
\"O69wtpsd*jj\",
\"+qrtpsdf;jj\",
\",etisdfg:jj\",
\"<tasdfgh>jj\",
\"o@#$%&=-Xjj\",
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\",
\"jjjjjjjjjjj\"
};
")

(defconst ide-skel-tree-widget-close-image
  (create-image ide-skel-tree-widget-close-xpm 'xpm t))

(define-widget 'ide-skel-imenu-internal-node-widget 'tree-widget
  "Internal node widget.")

(define-widget 'ide-skel-imenu-leaf-widget 'push-button
  "Leaf widget."
  :format        "%[%t%]\n"
  :button-face   'variable-pitch
  )

(defvar ide-skel-imenu-sorted nil)
(make-variable-buffer-local 'ide-skel-imenu-sorted)

(defvar ide-skel-imenu-editor-buffer nil)
(make-variable-buffer-local 'ide-skel-imenu-editor-buffer)

(defvar ide-skel-imenu-open-paths nil)
(make-variable-buffer-local 'ide-skel-imenu-open-paths)

(defface imenu-side-view-face '((t :inherit variable-pitch :height 0.8))
  "Default face used in right view for imenu"
  :group 'ide-skel)

(define-widget 'ide-skel-info-tree-dir-widget 'tree-widget
  "Directory Tree widget."
  :expander 'ide-skel-info-tree-expand-dir
  :notify 'ide-skel-info-open
  :indent   0)

(define-widget 'ide-skel-info-tree-file-widget 'push-button
  "File widget."
  :format        "%[%t%]%d\n"
  :button-face   'variable-pitch
  :notify        'ide-skel-info-file-open)

(defvar ide-skel-info-open-paths nil)
(make-variable-buffer-local 'ide-skel-info-open-paths)

(defvar ide-skel-info-root-node nil)
(make-variable-buffer-local 'ide-skel-info-root-node)

(defvar ide-skel-info-buffer nil)

(define-widget 'ide-skel-dir-tree-dir-widget 'tree-widget
  "Directory Tree widget."
  :expander 'ide-skel-dir-tree-expand-dir
  :notify 'ide-skel-dir-open
  :indent   0)

(define-widget 'ide-skel-dir-tree-file-widget 'push-button
  "File widget."
  :format        "%[%t%]%d\n"
  :button-face   'variable-pitch
  :notify        'ide-skel-file-open)

(defvar ide-skel-dir-open-paths nil)
(make-variable-buffer-local 'ide-skel-dir-open-paths)

(defvar ide-skel-dir-root-dir "/")
(make-variable-buffer-local 'ide-skel-dir-root-dir)

(defvar ide-skel-dir-buffer nil)

(defconst ide-skel-cvs-dir-regexp "\\(\\.svn\\|CVS\\)$")

(defstruct ide-skel-project
  root-path
  include-file-path ; for PC-include-file-path variable
)

(defvar ide-skel-projects nil)

(defvar ide-skel-proj-find-results-buffer-name "*Proj find*")

(defvar ide-skel-project-menu
  '("Project"
    :filter ide-skel-project-menu)
  "Menu for CVS/SVN projects")

(defvar ide-skel-proj-find-project-files-history nil)
(defvar ide-skel-proj-grep-project-files-history nil)

(defvar ide-skel-proj-ignored-extensions '("semantic.cache"))

(defvar ide-skel-all-text-files-flag nil)

(defvar ide-skel-proj-grep-header nil)

(defvar ide-skel-proj-old-compilation-exit-message-function nil)
(make-variable-buffer-local 'ide-skel-proj-old-compilation-exit-message-function)

(defvar ide-skel-proj-grep-mode-map nil)

(defvar ide-skel-proj-grep-replace-history nil)

;;;

(copy-face 'mode-line 'mode-line-inactive)

(define-key tree-widget-button-keymap [drag-mouse-1] 'ignore)

(defun ide-skel-tabbar-tab-label (tab)
  "Return a label for TAB.
That is, a string used to represent it on the tab bar."
  (let* ((object (tabbar-tab-value tab))
	 (tabset (tabbar-tab-tabset tab))
	 (label (format " %s "
			(or (and (bufferp object)
				 (with-current-buffer object ide-skel-tabbar-tab-label)) ; local in buffer
			    object))))
    (when (and (not (memq tabset (list (tabbar-get-tabset ide-skel-left-view-window-tabset-name)
				       (tabbar-get-tabset ide-skel-right-view-window-tabset-name))))
	       (numberp ide-skel-tabbar-tab-label-max-width)
	       (> ide-skel-tabbar-tab-label-max-width 0))
      (setq label (tabbar-shorten label ide-skel-tabbar-tab-label-max-width)))
    label))

(defun ide-skel-tabbar-help-on-tab (tab)
  "Return the help string shown when mouse is onto TAB."
  (let ((tabset (tabbar-tab-tabset tab))
	(object (tabbar-tab-value tab)))
    (or (when (bufferp object)
	  (with-current-buffer object
	    (or ide-skel-tabbar-tab-help-string ; local in buffer
		(buffer-file-name))))
	"mouse-1: switch to buffer\nmouse-2: delete other windows\nmouse-3: kill buffer")))

(defun ide-skel-tabbar-buffer-groups ()
  "Return the list of group names the current buffer belongs to."
   (if (and (ide-skel-side-view-buffer-p (current-buffer))
	    (or (not ide-skel-tabbar-tab-label)
		(not ide-skel-tabbar-enabled)))
       nil
    (let ((result (list (or ide-skel-tabset-name ; local in current buffer
			    (when (ide-skel-bottom-view-buffer-p (current-buffer)) ide-skel-bottom-view-window-tabset-name)
			    ide-skel-editor-window-tabset-name))))
      (dolist (window (copy-list (window-list nil 1)))
	(when (eq (window-buffer window) (current-buffer))
	  (let ((tabset-name (ide-skel-get-tabset-name-for-window window)))
	    (unless (member tabset-name result)
	      (push tabset-name result)))))
      result)))

(defun ide-skel-tabbar-buffer-tabs ()
  "Return the buffers to display on the tab bar, in a tab set."
  ;; (message "ide-skel-tabbar-buffer-tabs %S" (current-buffer))
  (tabbar-buffer-update-groups)
  (let* ((window (selected-window))
	 (tabset (tabbar-get-tabset (ide-skel-get-tabset-name-for-window window))))
     (when (not (tabbar-get-tab (current-buffer) tabset))
       (tabbar-add-tab tabset (current-buffer) t))
    (tabbar-select-tab-value (current-buffer) tabset)
    tabset))

(defun ide-skel-tabbar-buffer-list ()
  "Return the list of buffers to show in tabs.
The current buffer is always included."
  ;(ide-skel-tabbar-faces-adapt)
  (delq t
        (mapcar #'(lambda (b)
		    (let ((buffer-name (buffer-name b)))
		      (cond
		       ((and (ide-skel-side-view-buffer-p b) 
			     (with-current-buffer b
			       (or (not ide-skel-tabbar-tab-label)
				   (not ide-skel-tabbar-enabled))))
			t)
		       ;; Always include the current buffer.
		       ((eq (current-buffer) b) b)
		       ;; accept if buffer has tabset name
		       ((with-current-buffer b ide-skel-tabset-name) b)
		       ;; remove if matches any regexp from ide-skel-tabbar-hidden-buffer-names-regexp-list
		       ((not (null (some (lambda (regexp)
					   (string-match regexp buffer-name))
					 ide-skel-tabbar-hidden-buffer-names-regexp-list)))
			t)
		       ;; accept if buffer has filename
		       ((buffer-file-name b) b)
		       ;; remove if name starts with space
		       ((and (char-equal ?\  (aref (buffer-name b) 0))
			     (not (ide-skel-side-view-buffer-p b)))
			t)
		       ;; accept otherwise
		       (b))))
		(buffer-list (selected-frame)))))

(defun ide-skel-get-tabset-name-for-window (window)
  (cond ((eq (ide-skel-get-left-view-window) window) ide-skel-left-view-window-tabset-name)
	((eq (ide-skel-get-right-view-window) window) ide-skel-right-view-window-tabset-name)
        ((eq (ide-skel-get-bottom-view-window) window) ide-skel-bottom-view-window-tabset-name)
	(t ide-skel-editor-window-tabset-name)))

(defun ide-skel-tabbar-select-tab (event tab)
  "On mouse EVENT, select TAB."
  (let* ((mouse-button (event-basic-type event))
	 (buffer (tabbar-tab-value tab))
	 (tabset-name (and (buffer-live-p buffer)
			   (with-current-buffer buffer ide-skel-tabset-name)))
	 (left-tabset (equal tabset-name ide-skel-left-view-window-tabset-name))
	 (right-tabset (equal tabset-name ide-skel-right-view-window-tabset-name)))
    (cond
     ((eq mouse-button 'mouse-1)
      (cond (left-tabset (ide-skel-side-window-switch-to-buffer ide-skel-current-left-view-window buffer))
	    (right-tabset (ide-skel-side-window-switch-to-buffer ide-skel-current-right-view-window buffer))
	    (t (switch-to-buffer buffer))))
     ((and (eq mouse-button 'mouse-2)
	   (not left-tabset)
	   (not right-tabset))
      (switch-to-buffer buffer)
      (delete-other-windows))
     ((and (eq mouse-button 'mouse-3)
	   (not left-tabset)
	   (not right-tabset))
      (kill-buffer buffer)))
    ;; Disable group mode.
    (set 'tabbar-buffer-group-mode nil)))

(defun ide-skel-tabbar-buffer-kill-buffer-hook ()
  "Hook run just before actually killing a buffer.
In Tabbar mode, try to switch to a buffer in the current tab bar,
after the current buffer has been killed.  Try first the buffer in tab
after the current one, then the buffer in tab before.  On success, put
the sibling buffer in front of the buffer list, so it will be selected
first."
  (let ((buffer-to-kill (current-buffer)))
    (save-selected-window
      (save-current-buffer
	;; cannot kill buffer from any side view window
	(when (and (eq header-line-format tabbar-header-line-format)
		   (not (ide-skel-side-view-buffer-p (current-buffer))))
          (dolist (window (copy-list (window-list nil 1)))
            (when (eq buffer-to-kill (window-buffer window))
              (select-window window)
              (let ((bl (tabbar-tab-values (funcall tabbar-current-tabset-function)))
                    found sibling)
                (while (and bl (not found))
                  (if (equal buffer-to-kill (car bl))
                      (setq found t)
                    (setq sibling (car bl)))
                  (setq bl (cdr bl)))
		(setq sibling (or sibling (car bl)))
                (if (and sibling
			 (not (eq sibling buffer-to-kill))
			 (buffer-live-p sibling))
                    ;; Move sibling buffer in front of the buffer list.
		    (switch-to-buffer sibling)
                  (let ((next-buffer (ide-skel--find-buffer-for-bottom-view-window)))
                    (when (eq next-buffer buffer-to-kill)
                      (setq next-buffer (some (lambda (buf)
                                                (if (or (eq buf buffer-to-kill)
							(ide-skel-side-view-buffer-p buf)
                                                        (ide-skel-hidden-buffer-name-p (buffer-name buf)))
                                                    nil
                                                  buf))
                                              (buffer-list (selected-frame)))))
                    (when next-buffer
                      (switch-to-buffer next-buffer)
                      (tabbar-current-tabset t))))))))))))

(defun ide-skel-tabbar-inhibit-function ()
  "Inhibit display of the tab bar in specified windows, that is
in `checkdoc' status windows and in windows with its own header
line."
  (let ((result (tabbar-default-inhibit-function))
	(sw (selected-window)))
    (when (and result
	       (ide-skel-side-view-window-p sw))
      (setq result nil))
    (when (not (eq header-line-format tabbar-header-line-format))
      (setq result t))
    result))

(defun ide-skel-tabbar-home-function (event)
  (let* ((window (posn-window (event-start event)))
	 (is-view-window (ide-skel-side-view-window-p window))
	 (buffer (window-buffer window))
	 extra-commands
	 (normal-window-counter 0))
    (dolist (win (copy-list (window-list nil 1)))
      (unless (ide-skel-side-view-window-p win)
	(incf normal-window-counter)))
    (with-selected-window window
      (when (and is-view-window
		 ide-skel-tabbar-menu-function)
	(setq extra-commands (funcall ide-skel-tabbar-menu-function)))
      (let ((close-p (when (or is-view-window
			       (> normal-window-counter 1))
		       (list '(close "Close" t))))
	    (maximize-p (when (and (not is-view-window)
				   (> normal-window-counter 1))
			  (list '(maximize "Maximize" t)))))
	(when (or close-p maximize-p)
	  (let ((user-selection
		 (car (x-popup-menu event (append (list 'keymap) close-p maximize-p extra-commands)))))
	    (cond ((eq user-selection 'close)
		   (call-interactively 'delete-window))
		  ((eq user-selection 'maximize)
		   (delete-other-windows window))
		  ((eq user-selection nil))
		  (t
		   (funcall user-selection)))))))))

(defun ide-skel-tabbar-mwheel-scroll-forward (event)
  (interactive "@e")
  (tabbar-press-scroll-left))

(defun ide-skel-tabbar-mwheel-scroll-backward (event)
  (interactive "@e")
  (tabbar-press-scroll-right))

(defun ide-skel-tabbar-mwheel-scroll (event)
  "Select the next or previous group of tabs according to EVENT."
  (interactive "@e")
  (if (tabbar--mwheel-up-p event)
      (ide-skel-tabbar-mwheel-scroll-forward event)
    (ide-skel-tabbar-mwheel-scroll-backward event)))

(defun ide-skel-tabbar-mwhell-mode-hook ()
  (setq tabbar-mwheel-mode-map 
	(let ((km (make-sparse-keymap)))
	  (if (get 'mouse-wheel 'event-symbol-elements)
	      ;; Use one generic mouse wheel event
	      (define-key km [A-mouse-wheel]
		'ide-skel-tabbar-mwheel-scroll)
	    ;; Use separate up/down mouse wheel events
	    (let ((up   (tabbar--mwheel-key tabbar--mwheel-up-event))
		  (down (tabbar--mwheel-key tabbar--mwheel-down-event)))
	      (define-key km `[header-line ,down]
		'ide-skel-tabbar-mwheel-scroll-backward)
	      (define-key km `[header-line ,up]
		'ide-skel-tabbar-mwheel-scroll-forward)
	      ))
	  km))
  (setcdr (assoc 'tabbar-mwheel-mode minor-mode-map-alist) tabbar-mwheel-mode-map))

(defun ide-skel-tabbar-mode-hook ()
  (setq tabbar-prefix-map
	(let ((km (make-sparse-keymap)))
	  (define-key km [(control home)]  'tabbar-press-home)
	  (define-key km [(control left)]  'tabbar-backward)
	  (define-key km [(control right)] 'tabbar-forward)
	  (define-key km [(control prior)] 'tabbar-press-scroll-left)
	  (define-key km [(control next)]  'tabbar-press-scroll-right)
	  km))
  (setq tabbar-mode-map
	(let ((km (make-sparse-keymap)))
	  (define-key km tabbar-prefix-key tabbar-prefix-map)
	  km))
  (setcdr (assoc 'tabbar-mode minor-mode-map-alist) tabbar-mode-map))

(defun ide-skel-tabbar-init-hook ()
  (setq tabbar-cycle-scope 'tabs
	tabbar-auto-scroll-flag nil)
  (setq
   tabbar-tab-label-function 'ide-skel-tabbar-tab-label
   tabbar-help-on-tab-function 'ide-skel-tabbar-help-on-tab
   tabbar-buffer-groups-function 'ide-skel-tabbar-buffer-groups
   tabbar-buffer-list-function 'ide-skel-tabbar-buffer-list
   tabbar-current-tabset-function 'ide-skel-tabbar-buffer-tabs
   tabbar-select-tab-function 'ide-skel-tabbar-select-tab
   tabbar-inhibit-functions (append '(ide-skel-tabbar-inhibit-function) 
				    (delq 'tabbar-default-inhibit-function tabbar-inhibit-functions))
   tabbar-home-function 'ide-skel-tabbar-home-function
   tabbar-home-help-function (lambda () "Window menu"))
  (add-hook 'kill-buffer-hook 'ide-skel-tabbar-buffer-kill-buffer-hook))

(defun ide-skel-tabbar-quit-hook ()
  (setq
   tabbar-current-tabset-function nil
   tabbar-tab-label-function nil
   tabbar-select-tab-function nil
   tabbar-help-on-tab-function nil
   tabbar-home-function nil
   tabbar-home-help-function nil
   tabbar-buffer-groups-function nil
   tabbar-buffer-list-function nil)
  (remove-hook 'kill-buffer-hook 'ide-skel-tabbar-buffer-kill-buffer-hook))

(defun ide-skel-tabbar-load-hook ()
  (add-hook 'tabbar-mode-hook 'ide-skel-tabbar-mode-hook)
  (add-hook 'tabbar-mwheel-mode-hook 'ide-skel-tabbar-mwhell-mode-hook)
  (add-hook 'tabbar-init-hook 'ide-skel-tabbar-init-hook t)
  (add-hook 'tabbar-quit-hook 'ide-skel-tabbar-quit-hook t)
  ;(custom-set-faces
  ; '(tabbar-default ((t (:inherit variable-pitch :background "gray82" :foreground "gray50" :height 0.8))))
  ; '(tabbar-selected ((t (:inherit tabbar-default :background "white" :foreground "blue" :box (:line-width 1 :color "black")))))
  ; '(tabbar-separator ((t (:inherit tabbar-default :height 0.2))))
  ; '(tabbar-highlight ((t ())))
  ; '(tabbar-button-highlight ((t (:inherit tabbar-button))))
  ; '(tabbar-unselected ((t (:inherit tabbar-default :background "gray72" :foreground "black" :box (:line-width 1 :color "black"))))))
  ;(ide-skel-tabbar-faces-adapt)
)
  
;(defun ide-skel-tabbar-faces-adapt ()
;  (ide-skel-shine-face-background 'tabbar-default +18)
;  (set-face-attribute 'tabbar-selected nil :background (face-background 'default))
;  (set-face-attribute 'tabbar-selected nil :foreground (face-foreground 'font-lock-function-name-face))
;  (set-face-attribute 'tabbar-selected nil :box (list :line-width 1 :color (face-foreground 'default)))
;  (ide-skel-shine-face-background 'tabbar-unselected +30)
;  (set-face-attribute 'tabbar-unselected nil :foreground (face-foreground 'default))
;  (set-face-attribute 'tabbar-unselected nil :box (list :line-width 1 :color (face-foreground 'default)))
;  (ide-skel-shine-face-background 'tabbar-button +18)
;  (ide-skel-shine-face-foreground 'tabbar-button +20))

(defun ide-skel-paradox-settings ()
  ;; hide scroll buttons
  (setq tabbar-scroll-left-button (cons (cons "" nil) (cons "" nil))
	tabbar-scroll-right-button (cons (cons "" nil) (cons "" nil))))

(ide-skel-paradox-settings)


;;; Views

(defun ide-skel-window-list ()
  (delq nil 
	(mapcar (lambda (win)
		  (unless (memq win ide-skel-ommited-windows)
		    win))
		(copy-list (window-list nil 1)))))

(defun ide-skel-next-window (&optional window minibuf all-frames)
  (let ((nw (next-window window minibuf all-frames)))
    (if (memq nw ide-skel-ommited-windows)
	(ide-skel-next-window nw minibuf all-frames)
      nw)))

(defun ide-skel-previous-window (window minibuf all-frames)
  (let ((pw (previous-window window minibuf all-frames)))
    (if (memq pw ide-skel-ommited-windows)
	window
      pw)))

(defun ide-skel-win--absorb-win-node (dest-win-node src-win-node)
  (dotimes (index (length src-win-node))
    (setf (elt dest-win-node index)
	  (elt src-win-node index))))

(defun ide-skel-win--create-win-node (object)
  (cond ((win-node-p object) (copy-win-node object))
	((windowp object)
	 (make-win-node :corner-pos (ide-skel-win-corner object)
			:buf-corner-pos (window-start object)
			:buffer (window-buffer object)
			:horiz-scroll (window-hscroll object)
			:point (window-point object)
			:mark nil
			:edges (window-edges object)
			:fixed-size (cdr (assoc (ide-skel-win-corner object) ide-skel--fixed-size-windows))
			:divisions nil))
	(t (error "Argument is not win-not nor window: %S" object))))

(defun ide-skel-win--get-corner-pos (object)
  (cond ((windowp object) (ide-skel-win-corner object))
	((win-node-p object) (win-node-corner-pos object))
	((consp object) object)
	(t (error "Invalid arg: %S" object))))

(defun ide-skel-win--corner-pos-equal (win-node1 win-node2)
  (let ((corner-pos1 (ide-skel-win--get-corner-pos win-node1))
	(corner-pos2 (ide-skel-win--get-corner-pos win-node2)))
    (equal corner-pos1 corner-pos2)))

(defun ide-skel-win--add-division (win-node division &optional at-end-p)
  (setf (win-node-divisions win-node)
	(if at-end-p 
	    (reverse (cons division (reverse (win-node-divisions win-node))))
	  (cons division (win-node-divisions win-node)))))
      
(defun ide-skel-win--remove-division (win-node &optional from-end-p)
  (let (result)
    (if from-end-p
	(let ((divs (reverse (win-node-divisions win-node))))
	  (setq result (car divs))
	  (setf (win-node-divisions win-node)
		(reverse (cdr divs))))
      (setq result (car (win-node-divisions win-node)))
      (setf (win-node-divisions win-node) (cdr (win-node-divisions win-node))))
    result))

(defun ide-skel-win--find-node (root predicate)
  "Return node for which predicate returns non-nil."
  (when root
    (if	(funcall predicate root)
	root
      (some (lambda (division)
	      (ide-skel-win--find-node (division-win-node division) predicate))
	    (win-node-divisions root)))))

(defun ide-skel-win--find-node-by-corner-pos (root corner-pos)
  "Return struct for window with specified corner coordinates."
  (setq corner-pos
	(cond ((windowp corner-pos) (ide-skel-win-corner corner-pos))
	      ((consp corner-pos) corner-pos)
	      (t (error "arg corner-pos %S is not a pair/window" corner-pos))))
  (ide-skel-win--find-node root
		      (lambda (win-node)
			(equal corner-pos (win-node-corner-pos win-node)))))

(defun ide-skel-win--get-window-list ()
  (let* ((start-win (selected-window))
	 (cur-win (ide-skel-next-window start-win 1 1))
	 (win-list (list start-win)))
    (while (not (eq cur-win start-win))
      (setq win-list (cons cur-win win-list))
      (setq cur-win (ide-skel-next-window cur-win 1 1)))
    (reverse win-list)))

(defun ide-skel-win--analysis (&optional window-proc)
  ;; (message "ide-skel-win--analysis BEGIN %S" (get-internal-run-time))
  (let ((window-size-fixed nil))
    (setq ide-skel--fixed-size-windows nil)
    (dolist (window (copy-list (window-list nil 1)))
      (with-selected-window window
	(cond ((eq window-size-fixed 'width)
	       (push (cons (ide-skel-win-corner window) (cons (window-width window) nil)) ide-skel--fixed-size-windows))
	      ((eq window-size-fixed 'height)
	       (push (cons (ide-skel-win-corner window) (cons nil (window-height window))) ide-skel--fixed-size-windows))
	      ((not window-size-fixed)
	       nil)
	      (t
	       (push (cons (ide-skel-win-corner window) (cons (window-width window) (window-height window))) ide-skel--fixed-size-windows)))))
    (dolist (window (ide-skel-window-list))
      (when (ide-skel-side-view-window-p window) (set-window-dedicated-p window nil)))
    (setq ide-skel-win--minibuffer-selected-p (eq (selected-window) (minibuffer-window)))
    (when ide-skel-win--minibuffer-selected-p
      (select-window (ide-skel-get-editor-window)))
    (when (memq (selected-window) ide-skel-ommited-windows)
      (select-window (ide-skel-next-window (selected-window) 1 1)))
    (let* (leaf-win
	   (counter 0)
	   (cursor-alist (mapcar (lambda (win) (prog1 (cons win counter) (incf counter))) (ide-skel-win--get-window-list)))
	   win-node-set)
      (select-window (ide-skel-win-get-upper-left-window))
      (while (setq leaf-win (get-window-with-predicate
			     (lambda (win)
			       (if ide-skel-win--win2-switch (ide-skel-win--is-leaf2 win) (ide-skel-win--is-leaf win))) 1 1))
	(let* ((parent-win (ide-skel-previous-window leaf-win 1 1))
	       (parent-node (car (member* (ide-skel-win-corner parent-win) win-node-set :test 'ide-skel-win--corner-pos-equal)))
	       (leaf-node   (car (member* (ide-skel-win-corner leaf-win) win-node-set :test 'ide-skel-win--corner-pos-equal))))
	  (unless leaf-node
	    (setq leaf-node (ide-skel-win--create-win-node leaf-win))
	    (setf (win-node-cursor-priority leaf-node) (cdr (assq leaf-win cursor-alist)))
	    (setq win-node-set (adjoin leaf-node win-node-set :test 'ide-skel-win--corner-pos-equal)))
	  (unless parent-node
	    (setq parent-node (ide-skel-win--create-win-node parent-win))
	    (setf (win-node-cursor-priority parent-node) (cdr (assq parent-win cursor-alist)))
	    (setq win-node-set (adjoin parent-node win-node-set :test 'ide-skel-win--corner-pos-equal)))

	  (let* ((is-horizontal (ide-skel-win--is-adjacent parent-win 'right leaf-win))
		 (size (if is-horizontal (window-width parent-win) (window-height parent-win)))
		 percent)
	    (setf (win-node-edges leaf-node) (window-edges leaf-win))
	    (when window-proc (funcall window-proc parent-win))
	    (when window-proc (funcall window-proc leaf-win))
	    (delete-window leaf-win)
	    (when window-proc (funcall window-proc parent-win))
	    (setq percent
		  (/ (float size) (if is-horizontal (window-width parent-win) (window-height parent-win))))
	    (ide-skel-win--add-division parent-node
					(make-division :win-node leaf-node
						       :horizontal-p is-horizontal
						       :percent percent)))))
      ;; if there was only one window
      (unless win-node-set
	(when window-proc (funcall window-proc (selected-window)))
	(let ((node (ide-skel-win--create-win-node (selected-window))))
	  (setq win-node-set (adjoin node win-node-set
				     :test 'ide-skel-win--corner-pos-equal))))
      ;; return root node
      (let ((root-node (car (member* (ide-skel-win-corner (selected-window))
				     win-node-set
				     :test 'ide-skel-win--corner-pos-equal))))
	(setf (win-node-edges root-node) (window-edges (selected-window)))
	;; (message "ide-skel-win--analysis END %S" (get-internal-run-time))
	root-node))))

(defun ide-skel-win-get-upper-left-window ()
  "Return window in left upper corner"
  (let (best-window)
    (dolist (win (ide-skel-window-list))
      (if (null best-window)
          (setq best-window win)
        (let* ((best-window-coords (window-edges best-window))
               (best-window-weight (+ (car best-window-coords) (cadr best-window-coords)))
               (win-coords (window-edges win))
               (win-weight (+ (car win-coords) (cadr win-coords))))
          (when (< win-weight best-window-weight)
            (setq best-window win)))))
    best-window))

(defun ide--is-right-window (window)
  (let ((bounds (window-edges window))
	(result t))
    (dolist (win (ide-skel-window-list))
      (let ((left-edge-pos (car (window-edges win))))
        (when (>= left-edge-pos (nth 2 bounds))
          (setq result nil))))
    result))

(defun ide-skel-get-win-width-delta (window)
  (if window-system
      (let ((bounds (window-edges window)))
	(+ (- (- (nth 2 bounds) (nth 0 bounds)) (window-width window))
	   (if (and (not scroll-bar-mode)
			 (ide--is-right-window window))
	       1
	     0)))
    1))

(defun ide-skel-win--split (window horizontal-p percentage)
  "Split window and return children."
  (let* ((delta (ide-skel-get-win-width-delta window))
	 (weight percentage)
	 (new-size (cond
		    ((integerp weight) (if (< weight 0) 
					   (if horizontal-p
					       (+ (window-width window) weight)
					     (+ (window-height window) weight))
					 (if horizontal-p (+ delta weight) weight)))
		    (t			; float
		     (when (< weight 0.0)
		       (setq weight (+ 1.0 weight)))
		     (if horizontal-p
			 (round (+ delta (* (window-width window) weight)))
		       (round (* (window-height window) weight)))))))
       (split-window window new-size horizontal-p)))
       
(defun ide-skel-win--process-win-node (win win-node &optional window-proc)
  (let ((win2 win))
    (set-window-buffer win (win-node-buffer win-node))
					; (set-window-start win (win-node-buf-corner-pos win-node))
    (set-window-hscroll win (win-node-horiz-scroll win-node))
    (set-window-point win (win-node-point win-node))
    (when window-proc (setq win (funcall window-proc win)))
    (dolist (division (win-node-divisions win-node))
      (when (not (null (division-win-node division)))
	(let ((child-window (ide-skel-win--split win (division-horizontal-p division) (division-percent division))))
	  (when window-proc (setq win (funcall window-proc win)))
	  (ide-skel-win--process-win-node child-window (division-win-node division) window-proc))))
    (with-selected-window win2
      (let ((fixed-size (win-node-fixed-size win-node))
	    (window-size-fixed nil))
	(when fixed-size
	  (when (car fixed-size)
	    (enlarge-window (- (car fixed-size) (window-width win2)) t))
	  (when (cdr fixed-size)
	    (enlarge-window (- (cdr fixed-size) (window-height win2)) nil)))))
    (when (win-node-cursor-priority win-node)
      (unless sel-window
	(setq sel-window win
	      sel-priority (win-node-cursor-priority win-node)))
      (when (< (win-node-cursor-priority win-node) sel-priority)
	(setq sel-window win
	      sel-priority (win-node-cursor-priority win-node))))))
  
(defun ide-skel-win--synthesis (window win-node &optional window-proc)
  (let ((window-size-fixed nil)
	sel-window
	sel-priority)
    (ide-skel-win--process-win-node window win-node window-proc)
    (when sel-window
      (select-window sel-window))
    (when ide-skel-win--minibuffer-selected-p
      (select-window (minibuffer-window)))
    (setq ide-skel-win--minibuffer-selected-p nil)
    (dolist (window (ide-skel-window-list))
      (when (ide-skel-side-view-window-p window) (set-window-dedicated-p window t)))))

(defun ide-skel-win--remove-child (win-node child-win-node)
  (if (eq win-node child-win-node)
      (let* ((division (ide-skel-win--remove-division win-node t))
	     (divisions (win-node-divisions win-node)))
	(when division
	  (ide-skel-win--absorb-win-node win-node (division-win-node division)))
	(setf (win-node-divisions win-node)
	      (append divisions (win-node-divisions win-node))))
    (dolist (division (win-node-divisions win-node))
      (if (and (eq (division-win-node division) child-win-node) (null (win-node-divisions (division-win-node division))))
	  (setf (division-win-node division) nil)
	(ide-skel-win--remove-child (division-win-node division) child-win-node)))))

(defun ide-skel-win-remove-window (window)
  "Remove window with coordinates WINDOW."
  (let* ((window-corner-pos (ide-skel-win-corner window))
	 (root-win-node (ide-skel-win--analysis))
	 (child-win-node (ide-skel-win--find-node-by-corner-pos root-win-node window-corner-pos)))
    (ide-skel-win--remove-child root-win-node child-win-node)
    (ide-skel-win--synthesis (selected-window) root-win-node)))

(defun ide-skel-win-add-window (buffer parent-window-edges edge-symbol size)
  "Split PARENT-WINDOW-EDGES window along specified edge.  In new window with width/height SIZE
show buffer BUFFER.  SIZE can be integer (character count) or float 0.0 - 1.0."
  (when (windowp parent-window-edges)
    (setq parent-window-edges (window-edges parent-window-edges)))
  (let ((horizontal-p (or (eq edge-symbol 'left) (eq edge-symbol 'right)))
	(replace-parent-p (or (eq edge-symbol 'top) (eq edge-symbol 'left)))
	(percentage
	 (if (or (eq edge-symbol 'bottom) (eq edge-symbol 'right))
	     (- size)
	   size)))
    (ide-skel-win--add-window buffer parent-window-edges horizontal-p percentage replace-parent-p)))

(defun ide-skel-win--add-window (buffer parent-window-edges horizontal-p percentage replace-parent-p)
  (let* ((root-win-node (ide-skel-win--analysis))
	 (new-win-node (make-win-node :buffer buffer)))
    (ide-skel-win--synthesis (selected-window) root-win-node
			(lambda (window)
			  (if (equal (window-edges window) parent-window-edges)
			      (let ((child-window (ide-skel-win--split window horizontal-p percentage)))
				(set-window-buffer (if replace-parent-p window child-window) buffer)
				(if replace-parent-p child-window window))
			    window)))))

(defun ide-skel-win--get-bounds (object)
  (cond ((windowp object) (window-edges object))
	((and (listp object) (= (length object) 4)) object)
	(t (error "Invalid object param: %S" object))))

(defun ide-skel-win--win-area (window)
  (let ((win-bounds (ide-skel-win--get-bounds window)))
    (* (- (nth 2 win-bounds) (nth 0 win-bounds))
       (- (nth 3 win-bounds) (nth 1 win-bounds)))))

(defun ide-skel-win--is-adjacent(window1 edge-symbol window2)
  "Non-nil if WINDOW1 sticks to WINDOW2 along specified edge."
  (let ((bounds1 (ide-skel-win--get-bounds window1))
	(bounds2 (ide-skel-win--get-bounds window2))
	result)
    (if (or (equal edge-symbol 'top) (equal edge-symbol 'bottom))
	(setq result (and
		      (equal (nth 0 bounds1) (nth 0 bounds2))   ; bounds.LEFT = bounds2.LEFT
		      (equal (nth 2 bounds1) (nth 2 bounds2))))	; bounds.RIGHT = bounds2.RIGHT
      (setq result (and
		    (equal (nth 1 bounds1) (nth 1 bounds2))    ; bounds.TOP = bounds2.TOP
		    (equal (nth 3 bounds1) (nth 3 bounds2))))) ; bounds.BOTTOM = bounds2.BOTTOM
    (when result
      (setq result
	    (cond ((equal edge-symbol 'top) (equal (nth 1 bounds1) (nth 3 bounds2)))	; bounds.TOP = bounds2.BOTTOM
		  ((equal edge-symbol 'bottom) (equal (nth 3 bounds1) (nth 1 bounds2))) ; bounds.BOTTOM = bounds2.TOP
		  ((equal edge-symbol 'left) (equal (nth 0 bounds1) (nth 2 bounds2)))   ; bounds.LEFT = bounds2.RIGHT
		  (t (equal (nth 2 bounds1) (nth 0 bounds2))))))
    result))

(defun ide-skel-win--is-leaf (&optional window)
  "Non-nil if WINDOW is a leaf."
  (unless window
    (setq window (selected-window)))
  ;; no window can stick from right or bottom
  (when (and (not (get-window-with-predicate
		   (lambda (win) (ide-skel-win--is-adjacent window 'right win)) 1 1))
	     (not (get-window-with-predicate
		   (lambda (win) (ide-skel-win--is-adjacent window 'bottom win)) 1 1)))
    (let ((parent (ide-skel-previous-window window 1 1)))
      ;; parent must exist and come from left or up
      (when (and parent
		 (or (ide-skel-win--is-adjacent window 'top parent)
		     (ide-skel-win--is-adjacent window 'left parent)))
	window))))

(defun ide-skel-win--is-leaf2 (&optional win2)
  "Non-nil if WIN2 is leaf."
  (unless win2
    (setq win2 (selected-window)))
  ;; no window can stick from right or bottom
  (when (and (not (get-window-with-predicate
		   (lambda (win) (ide-skel-win--is-adjacent win2 'right win))))
	     (not (get-window-with-predicate
		   (lambda (win) (ide-skel-win--is-adjacent win2 'bottom win)))))
    (let ((parent (ide-skel-previous-window win2 1 1)))
      ;; parent must exist and come from left or up
      (when (and parent
		 (or (ide-skel-win--is-adjacent win2 'top parent)
		     (ide-skel-win--is-adjacent win2 'left parent)))
	win2))))

(defun ide-skel-win-corner (window)
  (let ((coords (window-edges window)))
    (cons (car coords) (cadr coords))))

(defun ide-skel-window-size-changed (frame)
  (let* ((editor-window (ide-skel-get-editor-window))
	 (left-view-window (car ide-skel--current-side-windows))
	 (right-view-window (cdr ide-skel--current-side-windows))
	 (bottom-view-window (ide-skel-get-bottom-view-window)))
    (ide-skel-recalculate-view-cache)
    (when bottom-view-window
      (ide-skel-remember-bottom-view-window))
    (when left-view-window
      (setq ide-skel-left-view-window-width (window-width left-view-window)))
    (when right-view-window
      (setq ide-skel-right-view-window-width (window-width right-view-window)))))
      
(add-hook 'window-size-change-functions 'ide-skel-window-size-changed)

(setq special-display-regexps ide-skel-bottom-view-buffer-names-regexps)

(defun ide-skel-recalculate-view-cache ()
  (setq ide-skel-selected-frame (selected-frame)
	ide-skel-current-editor-window (ide-skel-get-editor-window))
  (setq ide-skel-current-editor-buffer (window-buffer ide-skel-current-editor-window)
        ide-skel-current-left-view-window (car ide-skel--current-side-windows)
	ide-skel-current-right-view-window (cdr ide-skel--current-side-windows)))

(defun ide-skel-get-last-selected-window ()
  (and ide-skel-last-selected-window-or-buffer
       (or (and (window-live-p (car ide-skel-last-selected-window-or-buffer))
		(car ide-skel-last-selected-window-or-buffer))
	   (and (buffer-live-p (cdr ide-skel-last-selected-window-or-buffer))
		(get-buffer-window (cdr ide-skel-last-selected-window-or-buffer))))))

(require 'mwheel)

(defvar ide-skel-mouse-wheel-events (list mouse-wheel-up-event mouse-wheel-down-event))

(run-with-idle-timer 0 t (lambda ()
;; 			   (when ide-skel-current-left-view-window
;; 			     (with-selected-window ide-skel-current-left-view-window
;; 			       (beginning-of-line)))
;; 			   (when ide-skel-current-right-view-window
;; 			     (with-selected-window ide-skel-current-right-view-window
;; 			       (beginning-of-line)))
			   (unless (or (active-minibuffer-window)
				       (memq 'down (event-modifiers last-input-event))
				       (memq (event-basic-type last-input-event) ide-skel-mouse-wheel-events)
				       (mouse-movement-p last-input-event))
			     ;; selected frame changed?
			     (unless (eq (selected-frame) ide-skel-selected-frame)
			       (ide-skel-recalculate-view-cache))
			     ;; side view windows cannot have cursor
			     (while (memq (selected-window) (list ide-skel-current-left-view-window
								  ide-skel-current-right-view-window))
			       (let ((win (ide-skel-get-last-selected-window)))
				 (if (and win (not (eq (selected-window) win)))
				     (select-window win)
				   (other-window 1))))
			     (setq ide-skel-last-selected-window-or-buffer
				   (cons (selected-window) (window-buffer (selected-window))))
			     ;; current buffer changed?
			     (let ((editor-buffer (window-buffer ide-skel-current-editor-window)))
			       (when (not (eq ide-skel-last-buffer-change-event editor-buffer))
				 (ide-skel-send-event nil 'editor-buffer-changed ide-skel-last-buffer-change-event editor-buffer))))))

(setq special-display-function
      (lambda (buffer &optional data)
	(let ((bottom-view-window (ide-skel-get-bottom-view-window)))
	  (if (and bottom-view-window
		   (eq bottom-view-window (selected-window))
		   (member (buffer-name buffer) ide-skel-unexpected-bottom-view-window-buffer-names))
	      (progn
		(show-buffer (ide-skel-get-editor-window) buffer)
		(ide-skel-get-editor-window))
	    (unless (ide-skel-get-bottom-view-window)
	      (ide-skel-show-bottom-view-window))
	    (set-window-buffer (ide-skel-get-bottom-view-window) buffer)
	    ;; (select-window (ide-skel-get-bottom-view-window))
	    (ide-skel-get-bottom-view-window)))))

;;; Bottom view

(defun ide-skel-hidden-buffer-name-p (buffer-name)
  (equal (elt buffer-name 0) 32))

(defun ide-skel-bottom-view-buffer-p (buffer)
  "Non-nil if buffer should be shown in bottom view."
  (let ((name (buffer-name buffer)))
    (or (with-current-buffer buffer
	  (and ide-skel-tabset-name
	       (string= ide-skel-tabset-name ide-skel-bottom-view-window-tabset-name)))
	(and (not (ide-skel-hidden-buffer-name-p name))
	     (some (lambda (regexp) (string-match regexp name)) ide-skel-bottom-view-buffer-names-regexps)
	     (not (some (lambda (regexp) (string-match regexp name)) ide-skel-bottom-view-buffer-names-disallowed-regexps))))))

(defun ide-skel-remember-bottom-view-window ()
  (let ((bottom-view-window (ide-skel-get-bottom-view-window)))
    (when bottom-view-window
      (setq ide-skel--last-bottom-view-buffer-name (buffer-name (window-buffer bottom-view-window))
	    ide-skel-bottom-view-window-size (max 5 (window-height bottom-view-window))))))

(defun ide-skel--find-buffer-for-bottom-view-window ()
  "Returns first buffer to display in bottom view window (always returns a buffer)."
  (let ((best-buffers (list (car (buffer-list (selected-frame))))))
    (some (lambda (buffer)
	    (when (ide-skel-bottom-view-buffer-p buffer)
	      (if (member (buffer-name buffer) ide-skel-unexpected-bottom-view-window-buffer-names)
		  (setq best-buffers (append best-buffers (list buffer)))
		(setq best-buffers (cons buffer best-buffers)))
	      nil))
	  (buffer-list (selected-frame)))
    (if (and (not ide-skel-was-scratch)
	     (get-buffer "*scratch*"))
	(progn
	  (setq ide-skel-was-scratch t)
	  (get-buffer "*scratch*"))
    (car best-buffers))))

(defun ide-skel--is-full-width-window (window &rest except-windows)
  (let ((bounds (window-edges window))
	(result t))
    (dolist (win (ide-skel-window-list))
      (unless (memq win except-windows)
        (let ((left-edge-pos (car (window-edges win))))
          (when (or (< left-edge-pos (car bounds))
                    (>= left-edge-pos (nth 2 bounds)))
            (setq result nil)))))
    result))

(defun ide-skel-get-bottom-view-window ()
  (let* ((editor-window (ide-skel-get-editor-window))
	 best-window)
    ;; get lowest window
    (dolist (win (copy-list (window-list nil 1)))
      (when (with-current-buffer (window-buffer win)
	      (and (or (not ide-skel-tabset-name)
		       (equal ide-skel-tabset-name ide-skel-bottom-view-window-tabset-name))
		   (not (eq win editor-window))))
        (if (null best-window)
            (setq best-window win)
          (when (> (cadr (window-edges win)) (cadr (window-edges best-window)))
            (setq best-window win)))))
    (when (and best-window
	       (not (ide-skel--is-full-width-window best-window (ide-skel-get-left-view-window) (ide-skel-get-right-view-window))))
      (setq best-window nil))
    best-window))

(defun ide-skel-show-bottom-view-window (&optional buffer)
  (interactive)
  (unless ide-skel-bottom-view-window-oper-in-progress
    (let ((saved-window (cons (selected-window) (window-buffer (selected-window)))))
      (unwind-protect
	  (unless (ide-skel-get-bottom-view-window) ;; if not open yet
	    (setq ide-skel-bottom-view-window-oper-in-progress t)
	    (unless buffer
	      (setq buffer
		    (or (and ide-skel--last-bottom-view-buffer-name (get-buffer ide-skel--last-bottom-view-buffer-name))
			(ide-skel--find-buffer-for-bottom-view-window))))
	    (let* ((left-view-window (ide-skel-get-left-view-window))
		   (left-view-window-bounds (and left-view-window
						 (window-edges left-view-window)))
		   (right-view-window (ide-skel-get-right-view-window))
		   (right-view-window-bounds (and right-view-window
						  (window-edges right-view-window)))
		   (root-win-node (ide-skel-win--analysis))
		   (window-bounds (window-edges (selected-window)))) ; bounds of maximized window (after analysis)
	      (when (and left-view-window-bounds (not ide-skel-bottom-view-on-left-view))
		(setf (nth 0 window-bounds) (nth 2 left-view-window-bounds)))
	      (when (and right-view-window-bounds (not ide-skel-bottom-view-on-right-view))
		(setf (nth 2 window-bounds) (nth 0 right-view-window-bounds)))
	      (ide-skel-win--synthesis (selected-window) root-win-node)
	      (let ((ide-skel-win--win2-switch (and (not (null left-view-window))
						    ide-skel-bottom-view-on-right-view))
		    (old ide-skel-ommited-windows))
		(when (and (not ide-skel-bottom-view-on-left-view)
			   (not ide-skel-bottom-view-on-right-view)
			   (ide-skel-get-left-view-window))
		  (push (ide-skel-get-left-view-window) ide-skel-ommited-windows))
		(ide-skel-win-add-window buffer window-bounds 'bottom ide-skel-bottom-view-window-size)
		(setq ide-skel-ommited-windows old))))
	(if (window-live-p (car saved-window))
	    (select-window (car saved-window))
	  (when (get-buffer-window (cdr saved-window))
	      (select-window (get-buffer-window (cdr saved-window)))))
	(setq ide-skel-bottom-view-window-oper-in-progress nil)))))

(defun ide-skel-hide-bottom-view-window ()
  (interactive)
  (unless ide-skel-bottom-view-window-oper-in-progress
    (setq ide-skel-bottom-view-window-oper-in-progress t)
    (let ((bottom-view-window (ide-skel-get-bottom-view-window)))
      (when bottom-view-window
	(let ((ide-skel-win--win2-switch nil)
	      (select-editor (eq bottom-view-window (selected-window))))
	  (ide-skel-remember-bottom-view-window)
	  (ide-skel-win-remove-window bottom-view-window)
	  (when select-editor (select-window (ide-skel-get-editor-window))))))
    (setq ide-skel-bottom-view-window-oper-in-progress nil)))

(defun ide-skel-toggle-bottom-view-window ()
  "Toggle bottom view window."
  (interactive)
  (if (ide-skel-get-bottom-view-window)
      (ide-skel-hide-bottom-view-window)
    (ide-skel-show-bottom-view-window)))

;;; Editor

(defun ide-skel-get-editor-window ()
  (let (best-window)
    (setq ide-skel--current-side-windows (cons nil nil))
    (dolist (win (copy-list (window-list nil 1)))
      (when (with-current-buffer (window-buffer win)
	      (when (equal ide-skel-tabset-name ide-skel-left-view-window-tabset-name)
		(setcar ide-skel--current-side-windows win))
	      (when (equal ide-skel-tabset-name ide-skel-right-view-window-tabset-name)
		(setcdr ide-skel--current-side-windows win))
	      (or (not ide-skel-tabset-name)
		  (equal ide-skel-tabset-name ide-skel-editor-window-tabset-name)))
        (if (null best-window)
            (setq best-window win)
          (let* ((best-window-coords (window-edges best-window))
                 (win-coords (window-edges win)))
            (when (or (< (cadr win-coords) (cadr best-window-coords))
                      (and (= (cadr win-coords) (cadr best-window-coords))
                           (< (car win-coords) (car best-window-coords))))
              (setq best-window win))))))
    best-window))

;;; Left view & Right view

(defun ide-skel-toggle-side-view-window (name &optional run-hooks)
  (if (funcall (intern (format "ide-skel-get-%s-view-window" name)))
      (funcall (intern (format "ide-skel-hide-%s-view-window" name)) run-hooks)
    (funcall (intern (format "ide-skel-show-%s-view-window" name)) run-hooks)))

(defun ide-skel-toggle-left-view-window ()
  (interactive)
  (ide-skel-toggle-side-view-window 'left (called-interactively-p 'any)))

(defun ide-skel-toggle-right-view-window ()
  (interactive)
  (ide-skel-toggle-side-view-window 'right (called-interactively-p 'any)))


(add-hook 'kill-buffer-hook (lambda ()
			      (when (eq ide-skel-current-editor-buffer (current-buffer))
				(let* ((context (gethash ide-skel-current-editor-buffer ide-skel-context-properties))
				       (imenu-buffer (cdr (assq :imenu-buffer context)))
				       (imenu-window (when imenu-buffer (get-buffer-window imenu-buffer))))
				  (when imenu-window
				    (set-window-dedicated-p imenu-window nil)
				    (set-window-buffer imenu-window ide-skel-default-right-view-buffer) 
				    (set-window-dedicated-p imenu-window t))
				  (remhash (current-buffer) ide-skel-context-properties)
				  (when imenu-buffer
				    (kill-buffer imenu-buffer))))))

(defun ide-skel-send-event (side-symbol event-type &rest params)
  (ide-skel-recalculate-view-cache)
  (cond ((eq event-type 'hide)
	 (run-hook-with-args-until-success 'ide-skel-side-view-window-functions side-symbol 'hide)
	 (ide-skel-disable-nonactual-side-view-tabs side-symbol 'disable-all))
	((eq event-type 'show)
	 (run-hook-with-args-until-success 'ide-skel-side-view-window-functions side-symbol 'show)
	 (ide-skel-side-window-switch-to-buffer (symbol-value (intern (format "ide-skel-current-%s-view-window" side-symbol))) nil))
	((eq event-type 'editor-buffer-changed)
	 (run-hooks 'ide-skel-editor-buffer-changed-hook)
	 (when ide-skel-current-left-view-window
	   (ide-skel-disable-nonactual-side-view-tabs 'left)
	   (run-hook-with-args-until-success 'ide-skel-side-view-window-functions
					     'left 'editor-buffer-changed
					     ide-skel-last-buffer-change-event ide-skel-current-editor-buffer)
	   (ide-skel-side-window-switch-to-buffer ide-skel-current-left-view-window nil))
	 (when ide-skel-current-right-view-window
	   (ide-skel-disable-nonactual-side-view-tabs 'right)
	   (run-hook-with-args-until-success 'ide-skel-side-view-window-functions
					     'right 'editor-buffer-changed
					     (car params) (cadr params))
	   (ide-skel-side-window-switch-to-buffer ide-skel-current-right-view-window nil))
	 (setq ide-skel-last-buffer-change-event ide-skel-current-editor-buffer))
	((eq event-type 'tab-change)
	 (run-hook-with-args-until-success 'ide-skel-side-view-window-functions side-symbol 'tab-change (car params) (cadr params)))))

(defun ide-skel-hide-side-view-window (name &optional run-hooks)
  (let* ((view-window (funcall (intern (format "ide-skel-get-%s-view-window" name))))
	 (select-editor (eq view-window (selected-window))))
    (when view-window
      (when (active-minibuffer-window)
	(error "Cannot remove side window while minibuffer is active"))
      (let* ((bottom-view-window (ide-skel-get-bottom-view-window))
	     (selected-bottom-view-window (and bottom-view-window (eq bottom-view-window (selected-window))))
	     (buffer (window-buffer view-window))
	     (second-side-window (funcall (intern (format "ide-skel-get-%s-view-window" (if (eq name 'left) 'right 'left))))))
	(set (intern (format "ide-skel-last-%s-view-buffer" name)) buffer)
	(when run-hooks
	  (ide-skel-send-event name 'hide))
 	(when bottom-view-window
 	  (ide-skel-hide-bottom-view-window))
	(when second-side-window
	  (push second-side-window ide-skel-ommited-windows))
	(let ((ide-skel-win--win2-switch (eq name 'left)))
	  (set (intern (format "ide-skel-%s-view-window-width" name)) (window-width view-window))
	  (ide-skel-win-remove-window view-window))
	(setq ide-skel-ommited-windows nil)
 	(when bottom-view-window
 	  (ide-skel-show-bottom-view-window)
	  (when selected-bottom-view-window
	    (select-window (ide-skel-get-bottom-view-window))))
	(ide-skel-recalculate-view-cache)
	(when select-editor (select-window (ide-skel-get-editor-window)))))))

(defun ide-skel-hide-left-view-window (&optional run-hooks)
  (interactive)
  (let ((right-view-window (ide-skel-get-right-view-window)))
    (when right-view-window
      (ide-skel-hide-right-view-window))
    (ide-skel-hide-side-view-window 'left (or run-hooks (called-interactively-p 'any)))
    (when right-view-window
      (ide-skel-show-right-view-window))))

(defun ide-skel-hide-right-view-window (&optional run-hooks)
  (interactive)
  (ide-skel-hide-side-view-window 'right (or (called-interactively-p 'any) run-hooks)))

(defun ide-skel-get-side-view-buffer-create (name side-sym tab-label help-string keep-condition-function)
  (let* ((was-buffer (get-buffer name))
	 (km (make-sparse-keymap))
	 (buffer (get-buffer-create name)))
    (unless was-buffer
      (with-current-buffer buffer
	(kill-all-local-variables)
	(remove-overlays)
	(define-key km [drag-mouse-1] 'ignore)
	(use-local-map km)
	(make-local-variable 'mouse-wheel-scroll-amount)
	(make-local-variable 'auto-hscroll-mode)
	(make-local-variable 'hscroll-step)
	(make-local-variable 'hscroll-margin)
	(setq ide-skel-tabset-name (if (eq side-sym 'left) ide-skel-left-view-window-tabset-name ide-skel-right-view-window-tabset-name)
	      ide-skel-tabbar-tab-label tab-label
	      ide-skel-tabbar-tab-help-string help-string
	      ide-skel-keep-condition-function keep-condition-function
	      auto-hscroll-mode nil
	      hscroll-step 0.0
	      hscroll-margin 0
;; 	      left-fringe-width 0
;; 	      right-fringe-width 0
	      buffer-read-only t
	      mode-line-format " "
	      mouse-wheel-scroll-amount '(1)
	      window-size-fixed 'width)
	;; (make-variable-buffer-local 'fringe-indicator-alist)
	(setq fringe-indicator-alist (copy-alist default-fringe-indicator-alist))
;; 	(when (>= emacs-major-version 22)
;; 	  (set 'indicate-buffer-boundaries '((up . left) (down . left))))
	(setcdr (assq 'truncation fringe-indicator-alist) nil)
	(set (make-local-variable 'scroll-conservatively) 1500) ; much greater than 0
	(when (and window-system
		   (not ide-skel-side-view-display-cursor))
	  (setq cursor-type nil))))
    buffer))

(defvar ide-skel-default-left-view-buffer
  (let ((buffer (ide-skel-get-side-view-buffer-create " Default Left View Buffer" 'left nil nil (lambda (buf) t))))
    (with-current-buffer buffer
      (setq header-line-format " "))
    buffer))
(defvar ide-skel-last-left-view-buffer ide-skel-default-left-view-buffer)
(defvar ide-skel-default-right-view-buffer
  (let ((buffer (ide-skel-get-side-view-buffer-create " Default Right View Buffer" 'right nil nil (lambda (buf) t))))
    (with-current-buffer buffer
      (setq header-line-format " "))
    buffer))
(defvar ide-skel-last-right-view-buffer ide-skel-default-right-view-buffer)

(defun ide-skel-show-side-view-window (name &optional run-hooks)
  (unless (funcall (intern (format "ide-skel-get-%s-view-window" name)))
    (let* ((current-buffer (window-buffer (selected-window)))
	   (bottom-view-window (ide-skel-get-bottom-view-window))
	   root-win-node
	   (bottom-view-window-bounds (and (or (symbol-value (intern (format "ide-skel-bottom-view-on-%s-view" name)))
					       (and ide-skel-bottom-view-on-left-view
						    (not ide-skel-bottom-view-on-right-view)))
					   bottom-view-window
					   (window-edges bottom-view-window)))
	   best-window-bounds)
      (when bottom-view-window-bounds
 	(ide-skel-hide-bottom-view-window))
      (let ((second-side-window (funcall (intern (format "ide-skel-get-%s-view-window" (if (eq name 'left) 'right 'left))))))
	(when second-side-window
	  (push second-side-window ide-skel-ommited-windows))
	(setq root-win-node (ide-skel-win--analysis))
	(setq best-window-bounds (window-edges (selected-window))) ; bounds of maximized window (after analysis)
	(ide-skel-win--synthesis (selected-window) root-win-node)
	(ide-skel-win-add-window
	 (symbol-value (intern (format (if run-hooks "ide-skel-default-%s-view-buffer" "ide-skel-last-%s-view-buffer") name)))
	 best-window-bounds name
	 (symbol-value (intern (format "ide-skel-%s-view-window-width" name))))
	(setq ide-skel-ommited-windows nil)
 	(when bottom-view-window-bounds
 	  (ide-skel-show-bottom-view-window))
	(set-window-dedicated-p (funcall (intern (format "ide-skel-get-%s-view-window" name))) t)
	(when run-hooks
	  (dolist (tab (tabbar-tabs (tabbar-get-tabset (symbol-value (intern (format "ide-skel-%s-view-window-tabset-name" name))))))
	    (tabbar-delete-tab tab))
	  (ide-skel-send-event name 'show))
	(some (lambda (win) (when (eq (window-buffer win) current-buffer) (select-window win) t)) (copy-list (window-list nil 1)))))))

;; Disables from view all buffers for which keep condition function
;; returns nil.  If a current buffer is there, select another enabled,
;; which implies tab-change event, then select any enabled buffer.
(defun ide-skel-disable-nonactual-side-view-tabs (name &optional disable-all)
  (let* ((tabset (tabbar-get-tabset (symbol-value (intern (format "ide-skel-%s-view-window-tabset-name" name)))))
	 (tabs (tabbar-tabs tabset))
	 (editor-buffer (window-buffer (ide-skel-get-editor-window)))
	 selected-deleted
	 (selected-tab (tabbar-selected-tab tabset)))
    (when tabs
      (dolist (tab tabs)
	(let ((buffer (tabbar-tab-value tab)))
	  (with-current-buffer buffer
	    (when (or disable-all
		      (not ide-skel-keep-condition-function)
		      (not (funcall ide-skel-keep-condition-function editor-buffer)))
	      (setq ide-skel-tabbar-enabled nil)
	      (when (eq tab selected-tab)
		(setq selected-deleted t))
	      (tabbar-delete-tab tab)))))
      (let ((selected-buffer (when (and (not selected-deleted)
					(tabbar-tabs tabset) (tabbar-selected-value tabset)))))
	(when (and (not disable-all)
		   (or selected-deleted
		       (not (eq (tabbar-selected-tab tabset) selected-tab))))
	  (unless selected-buffer
	    (setq selected-buffer (symbol-value (intern (format "ide-skel-default-%s-view-buffer" name)))))
	  (ide-skel-side-window-switch-to-buffer
	   (symbol-value (intern (format "ide-skel-current-%s-view-window" name)))
	   selected-buffer))))))
	
(defun ide-skel-show-left-view-window (&optional run-hooks)
  (interactive)
  (let ((right-view-window (ide-skel-get-right-view-window)))
    (when right-view-window
      (ide-skel-hide-right-view-window))
    (ide-skel-show-side-view-window 'left (or run-hooks (called-interactively-p 'any)))
    (when right-view-window
      (ide-skel-show-right-view-window))))

(defun ide-skel-show-right-view-window (&optional run-hooks)
  (interactive)
  (ide-skel-show-side-view-window 'right (or run-hooks (called-interactively-p 'any))))

(defun ide-skel-get-side-view-window (name)
  (let ((tabset-name (symbol-value (intern (format "ide-skel-%s-view-window-tabset-name" name)))))
    (some (lambda (win)
	    (when (with-current-buffer (window-buffer win)
		    (equal ide-skel-tabset-name tabset-name))
	      win))
	  (copy-list (window-list nil 1)))))
  
(defun ide-skel-get-left-view-window ()
  (ide-skel-get-side-view-window 'left))

(defun ide-skel-get-right-view-window ()
  (ide-skel-get-side-view-window 'right))

(defun ide-skel-get-side-view-windows ()
  (let (result
	(left-view-win (ide-skel-get-left-view-window))
	(right-view-win (ide-skel-get-right-view-window)))
    (when left-view-win (push left-view-win result))
    (when right-view-win (push right-view-win result))
    result))

(defun ide-skel-side-view-window-p (window)
  (ide-skel-side-view-buffer-p (window-buffer window)))

(defun ide-skel-side-view-buffer-p (buffer)
  (with-current-buffer buffer
    (or (equal ide-skel-tabset-name ide-skel-left-view-window-tabset-name)
	(equal ide-skel-tabset-name ide-skel-right-view-window-tabset-name))))

(defadvice delete-window (around delete-window-around-advice (&optional window))
  (let* ((target-window (if window window (selected-window)))
	 (editor-window (and (called-interactively-p 'any) (ide-skel-get-editor-window))) ; for ide-skel--current-side-windows (side-effects)
	 (hide-view-windows (and (called-interactively-p 'any)
				 (not (eq (car ide-skel--current-side-windows) target-window))
				 (not (eq (cdr ide-skel--current-side-windows) target-window))))
	 (hide-left-view-window (and hide-view-windows (car ide-skel--current-side-windows)))
	 (hide-right-view-window (and hide-view-windows (cdr ide-skel--current-side-windows)))
	 result)
    (when (called-interactively-p 'any)
      (if (eq (car ide-skel--current-side-windows) target-window)
	  (ide-skel-send-event 'left 'hide)
	(when (eq (cdr ide-skel--current-side-windows) target-window)
	  (ide-skel-send-event 'right 'hide))))
    (let* ((edges (window-inside-edges window))
	   (buf (window-buffer window))
	   win
	   (center-position (cons (/ (+ (car edges) (caddr edges)) 2)
				  (/ (+ (cadr edges) (cadddr edges)) 2))))
      (when hide-left-view-window (ide-skel-hide-left-view-window))
      (when hide-right-view-window (ide-skel-hide-right-view-window))
      (setq win (window-at (car center-position) (cdr center-position)))
      (when (eq (window-buffer win) buf)
	(setq window (window-at (car center-position) (cdr center-position)))))
    (unwind-protect
	(setq result (progn ad-do-it))
      (when hide-left-view-window (ide-skel-show-left-view-window))
      (when hide-right-view-window (ide-skel-show-right-view-window)))
    result))
(ad-activate 'delete-window)

(defadvice delete-other-windows (around delete-other-windows-around-advice (&optional window))
  (ide-skel-assert-not-in-side-view-window)
  (let* ((editor-window (ide-skel-get-editor-window))
	 (dont-revert-after (and (called-interactively-p 'any) (listp current-prefix-arg) (car current-prefix-arg))) ; C-u
	 (hide-left-view-window (and (called-interactively-p 'any) (car ide-skel--current-side-windows)))
	 (hide-right-view-window (and (called-interactively-p 'any) (cdr ide-skel--current-side-windows)))
	 result)
    (when hide-left-view-window (ide-skel-hide-left-view-window dont-revert-after))
    (when hide-right-view-window (ide-skel-hide-right-view-window dont-revert-after))
    (unwind-protect
	(setq result (progn ad-do-it))
      (when (not dont-revert-after)
	(when hide-left-view-window
	  (ide-skel-show-left-view-window))
	(when hide-right-view-window
	  (ide-skel-show-right-view-window))))
    result))
(ad-activate 'delete-other-windows)

(defun ide-skel-assert-not-in-side-view-window ()
  (when (and (called-interactively-p 'any) (ide-skel-side-view-window-p (selected-window)))
    (error "Cannot do it")))

(defadvice kill-buffer (before kill-buffer-before-advice (buffer))
  (ide-skel-assert-not-in-side-view-window))
(ad-activate 'kill-buffer)

(defadvice split-window-vertically (before split-window-vertically-before-advice (&optional size))
  (ide-skel-assert-not-in-side-view-window))
(ad-activate 'split-window-vertically)

(defadvice split-window-horizontally (before split-window-horizontally-before-advice (&optional size))
  (ide-skel-assert-not-in-side-view-window))
(ad-activate 'split-window-horizontally)

(defadvice mouse-drag-vertical-line (around mouse-drag-vertical-line-around-advice (start-event))
  (let* ((editor-window (ide-skel-get-editor-window))
	 (left-view-window (car ide-skel--current-side-windows))
	 (right-view-window (cdr ide-skel--current-side-windows)))
    (when left-view-window (with-selected-window left-view-window (setq window-size-fixed nil)))
    (when right-view-window (with-selected-window right-view-window (setq window-size-fixed nil)))
    (unwind-protect
	(progn ad-do-it)
      (when left-view-window (with-selected-window left-view-window (setq window-size-fixed 'width)))
      (when right-view-window (with-selected-window right-view-window (setq window-size-fixed 'width))))))
(ad-activate 'mouse-drag-vertical-line)

(defadvice other-window (after other-window-after-advice (arg &optional all-frames))
  (if (memq (selected-window) (list ide-skel-current-left-view-window ide-skel-current-right-view-window))
      (other-window arg all-frames)
    ad-return-value))
(ad-activate 'other-window)

;; Buffer list buffer (left side view)

(define-derived-mode fundmental-mode
  fundamental-mode "Fundmental")

(setq default-major-mode 'fundmental-mode)

(defun ide-skel-recentf-closed-files-list ()
  "Lista ostatnio otwieranych, ale aktualnie zamknietych plikow"
  (let* ((open-file-paths (delq nil (mapcar (lambda (buffer) (buffer-file-name buffer)) (buffer-list)))))
    (if (featurep 'recentf)
	(sort (reverse (set-difference recentf-list open-file-paths :test 'string=))
	      (lambda (path1 path2)
		(string< (file-name-nondirectory path1) (file-name-nondirectory path2))))
      nil)))

(defun ide-skel-select-buffer (buffer-or-path &optional line-no)
  (let* ((window (ide-skel-get-last-selected-window))
	 (buffer (or (and (bufferp buffer-or-path) buffer-or-path)
		    (find-file-noselect buffer-or-path)))
	 (is-bottom-view-buffer (ide-skel-bottom-view-buffer-p buffer)))
    (when (not (buffer-live-p buffer))
      (error "Buffer %s is dead" buffer))
    (unless (get-buffer-window buffer)
      ;; (message "%S %S" window (ide-skel-get-bottom-view-window))
      (if (and window
	       (not (eq window (ide-skel-get-bottom-view-window)))
	       (not is-bottom-view-buffer))
	  (set-window-buffer window buffer)
	(let ((editor-window (ide-skel-get-editor-window)))
	  (select-window editor-window)
	  (if is-bottom-view-buffer
	      (switch-to-buffer-other-window buffer)
	    (set-window-buffer editor-window buffer)))))
    (setq ide-skel-last-selected-window-or-buffer (cons (get-buffer-window buffer) buffer))
    (select-window (car ide-skel-last-selected-window-or-buffer))
    (when line-no
      (with-current-buffer buffer
	(goto-line line-no)))))

(defun ide-skel-select-buffer-handler (event)
  (interactive "@e")
  ;; (message "EVENT: %S" event)
  (with-selected-window (posn-window (event-start event))
    (let* ((object (get-text-property (posn-point (event-start event)) 'object-to-display)))
      (beginning-of-line)
      (ide-skel-select-buffer object))))
    
(defun ide-skel-buffers-view-insert-buffer-list (label buffer-list)
  (setq label (propertize label 'face 'bold))
  (insert (format "%s\n" label))
  (dolist (object buffer-list)
    (let* ((label (format "  % -100s" (if (bufferp object) (buffer-name object) (file-name-nondirectory object))))
	   (km (make-sparse-keymap)))
      (define-key km [mouse-1] 'ide-skel-select-buffer-handler)
      (setq label (propertize label
			      'mouse-face 'ide-skel-highlight-face
			      'local-map km
			      'face 'variable-pitch
			      'pointer 'hand
			      'object-to-display object
			      'help-echo (if (bufferp object) (buffer-file-name object) object)))
      (insert label)
      (insert "\n"))))

(defun ide-skel-buffers-view-fill ()
  (when ide-skel-current-left-view-window
    (with-current-buffer ide-skel-buffer-list-buffer
      (let ((point (point))
	    (window-start (when (eq (window-buffer ide-skel-current-left-view-window) ide-skel-buffer-list-buffer)
			    (save-excursion
			      (goto-char (window-start ide-skel-current-left-view-window))
			      (cons (line-number-at-pos) (current-column))))))
	;; (message "%S" window-start)
	(let (asterisk-buffers
	      (inhibit-read-only t)
	      normal-buffers)
	  (erase-buffer)
	  (dolist (buffer (sort (buffer-list) (lambda (buf1 buf2) (string< (buffer-name buf1) (buffer-name buf2)))))
	    (let* ((name (buffer-name buffer))
		   (first-char (aref (buffer-name buffer) 0)))
	      (unless (char-equal ?\  first-char)
		(if (char-equal ?* first-char)
		    (push buffer asterisk-buffers)
		  (push buffer normal-buffers)))))
	  (ide-skel-buffers-view-insert-buffer-list "Normal Buffers:" normal-buffers)
	  (ide-skel-buffers-view-insert-buffer-list "Scratch Buffers:" asterisk-buffers)
	  (ide-skel-buffers-view-insert-buffer-list "Recent Files:" (ide-skel-recentf-closed-files-list)))
	(if window-start
	    (let ((pos (save-excursion
			 (goto-line (car window-start))
			 (beginning-of-line)
			 (forward-char (cdr window-start))
			 (point))))
	      (set-window-start ide-skel-current-left-view-window pos))
	  (goto-char point)
	  (beginning-of-line))))))

(defun ide-skel-some-view-window-buffer (side-symbol predicate)
  (some (lambda (buffer)
	  (and (buffer-live-p buffer)
	       (with-current-buffer buffer
		 (and (equal ide-skel-tabset-name (symbol-value (intern (format "ide-skel-%s-view-window-tabset-name" side-symbol))))
		      ide-skel-tabbar-enabled
		      (funcall predicate buffer)
		      buffer))))
	(buffer-list)))

(defun ide-skel-side-window-switch-to-buffer (side-window buffer)
  "If BUFFER is nil, then select any non-default buffer.  The
TAB-CHANGE event is send only if selected buffer changed."
  (unwind-protect
      (let* ((side-symbol (cond ((eq side-window ide-skel-current-left-view-window) 'left)
			       ((eq side-window ide-skel-current-right-view-window) 'right)
			       (t nil)))
	     (context (gethash ide-skel-current-editor-buffer ide-skel-context-properties))
	     (context-default-tab-label-symbol (intern (format "default-%s-tab-label" side-symbol))))
	(when side-symbol
	  (unless buffer
	    (let* ((default-empty-buffer (symbol-value (intern (format "ide-skel-default-%s-view-buffer" side-symbol))))
		   (context-default-tab-label (cdr (assq context-default-tab-label-symbol context)))
		   (last-view-window-tab-label (symbol-value (intern (format "ide-skel-last-%s-view-window-tab-label" side-symbol)))))
	      ;; first non-nil:
	      ;;  - selected before in this context
	      ;;  - selected in previous context
	      ;;  - current if other than default-empty
	      ;;  - first non default-empty
	      ;;  - default-empty
	      (setq buffer
		    (or (and context-default-tab-label
			     (ide-skel-some-view-window-buffer side-symbol (lambda (buffer)
									     (equal ide-skel-tabbar-tab-label context-default-tab-label))))
			(and last-view-window-tab-label
			     (ide-skel-some-view-window-buffer side-symbol (lambda (buffer)
									     (equal ide-skel-tabbar-tab-label last-view-window-tab-label))))
			(and (not (eq (window-buffer side-window) default-empty-buffer))
			     (window-buffer side-window))
			(ide-skel-some-view-window-buffer side-symbol (lambda (buffer) ide-skel-tabbar-tab-label))
			default-empty-buffer))))
	  (unless (eq (window-buffer side-window) buffer)
	    (set (intern (format "ide-skel-last-%s-view-window-tab-label" side-symbol)) (with-current-buffer buffer ide-skel-tabbar-tab-label))
	    (setq context (assq-delete-all context-default-tab-label-symbol context))
	    (puthash ide-skel-current-editor-buffer
		     (cons (cons context-default-tab-label-symbol (with-current-buffer buffer ide-skel-tabbar-tab-label)) context)
		     ide-skel-context-properties)
	    (ide-skel-send-event side-symbol 'tab-change (window-buffer side-window) buffer)))
	(set-window-dedicated-p side-window nil)
	(set-window-buffer side-window buffer))
    (set-window-dedicated-p side-window t)))

;; args: 'left/right 'show/editor-buffer-changed/hide/tab-change &rest buffer...
(defun ide-skel-default-side-view-window-function (side event &rest list)
  ;; (message "SIDE: %S, event: %S, rest: %S %S" side event list ide-skel-current-left-view-window)
  (when (and (eq side 'left) ide-skel-current-left-view-window)
    (cond ((eq event 'show)
	   (unless ide-skel-buffer-list-buffer
	     (setq ide-skel-buffer-list-buffer (ide-skel-get-side-view-buffer-create
						" Ide-Skel Buffer List Buffer" 'left "Bufs" "List of opened and recent files"
						(lambda (buf) t)))
	     (with-current-buffer ide-skel-buffer-list-buffer
	       (setq ide-skel-tabbar-enabled t)))
	   (ide-skel-buffers-view-fill)
	   (ide-skel-side-window-switch-to-buffer ide-skel-current-left-view-window ide-skel-buffer-list-buffer))))
  nil)
      
  ;; (message "SIDE: %S, event: %S, rest: %S" side event list)

(add-hook 'change-major-mode-hook (lambda () (setq ide-skel-buffer-list-tick t)))
(add-hook 'kill-buffer-hook (lambda () (setq ide-skel-buffer-list-tick t)))
(run-with-idle-timer 0.1 t (lambda ()
			   (when ide-skel-buffer-list-tick
			     (setq ide-skel-buffer-list-tick nil)
			     (ide-skel-buffers-view-fill))))

(add-hook 'ide-skel-side-view-window-functions 'ide-skel-default-side-view-window-function)

(define-key-after global-map [tool-bar ide-skel-toggle-left-view-window]
  (list 'menu-item "Left View Window" 'ide-skel-toggle-left-view-window :image ide-skel-left-view-window-image))
(define-key-after global-map [tool-bar ide-skel-toggle-bottom-view-window]
  (list 'menu-item "Bottom View Window" 'ide-skel-toggle-bottom-view-window :image ide-skel-bottom-view-window-image))
(define-key-after global-map [tool-bar ide-skel-toggle-right-view-window]
  (list 'menu-item "Right View Window" 'ide-skel-toggle-right-view-window :image ide-skel-right-view-window-image))

(eval-after-load "tabbar" '(ide-skel-tabbar-load-hook))

;;; Tree Widget

(defadvice tree-widget-lookup-image (around tree-widget-lookup-image-around-advice (name))
  (if (equal (tree-widget-theme-name) "small-folder")
      (setq ad-return-value (apply 'create-image (symbol-value (intern (format "ide-skel-tree-widget-%s-xpm" name))) 'xpm t (tree-widget-image-properties name)))
    ad-do-it))
(ad-activate 'tree-widget-lookup-image)



;;; Imenu

(require 'imenu)

(defun ide-skel-imenu-refresh ()
  (interactive)
  (ide-skel-imenu-side-view-draw-tree (window-buffer ide-skel-current-right-view-window) t))

(defun ide-skel-imenu-sort-change ()
  (interactive)
  (with-current-buffer (window-buffer ide-skel-current-right-view-window)
    (setq ide-skel-imenu-sorted (not ide-skel-imenu-sorted)))
  (ide-skel-imenu-side-view-draw-tree (window-buffer ide-skel-current-right-view-window) t))

(defun ide-skel-imenu-get-buffer-create (editor-buffer &optional dont-create)
  (let* ((context (gethash editor-buffer ide-skel-context-properties))
	 (buffer (cdr (assq :imenu-buffer context))))
    (when (and (not buffer) (not dont-create))
      (setq buffer (ide-skel-get-side-view-buffer-create (concat " " (buffer-name editor-buffer) " Ide Skel Imenu")
							 'right "Imenu" nil
							 (lambda (editor-buffer)
							   (eq ide-skel-imenu-editor-buffer ide-skel-current-editor-buffer))))
      (with-current-buffer buffer
	(setq ide-skel-tabbar-menu-function
	      (lambda ()
		(let ((is-outline-mode (with-current-buffer (window-buffer ide-skel-current-right-view-window)
					 (with-current-buffer ide-skel-imenu-editor-buffer
					   (or (eq major-mode 'outline-mode)
					       (and (boundp 'outline-minor-mode)
						    (symbol-value 'outline-minor-mode)))))))
		  (append
		   (list
		    (list 'ide-skel-imenu-refresh "Refresh" t)
		    (unless is-outline-mode
		      (list 'ide-skel-imenu-sort-change (if (with-current-buffer (window-buffer ide-skel-current-right-view-window)
							      ide-skel-imenu-sorted)
							    "Natural order"
							  "Sorted order") t))))))
	      ide-skel-imenu-editor-buffer editor-buffer
	      ide-skel-imenu-open-paths (make-hash-table :test 'equal))
	(add-hook 'tree-widget-after-toggle-functions (lambda (widget)
							(let ((path (widget-get widget :path)))
							  (when path
							    (if (widget-get widget :open)
								(puthash path t ide-skel-imenu-open-paths)
							      (remhash path ide-skel-imenu-open-paths)))))
		  nil t))
      (puthash editor-buffer (cons (cons :imenu-buffer buffer) context) ide-skel-context-properties))
    buffer))

(defun ide-skel-tree-node-notify (widget &rest rest)
  (let ((index-name (widget-get widget :index-name))
	(index-position (widget-get widget :index-position))
	(function (widget-get widget :function))
	(arguments (widget-get widget :arguments)))
    (select-window (ide-skel-get-editor-window))
    (if function
	(apply function index-name index-position arguments)
      (goto-char index-position))))

;; building hash
(defun ide-skel-imenu-analyze (hash prefix element)
  (when element
    (if (and (consp (cdr element))
	     (listp (cadr element)))
	(dolist (elem (cdr element))
	  (ide-skel-imenu-analyze hash (concat prefix "/" (car element)) elem))
      (puthash (concat prefix "/" (car element)) (list (cons :element element)) hash))))

;; logical linking, internal nodes creation
(defun ide-skel-imenu-analyze2 (hash prefix element)
  (when element
    (if (and (consp (cdr element))
	     (listp (cadr element)))
	(dolist (elem (cdr element))
	  (ide-skel-imenu-analyze2 hash (concat prefix "/" (car element)) elem))
      (let* ((index-name (car element))
	     (path (concat prefix "/" index-name))
	     (node (gethash path hash))
	     (reverse-separators (let ((index 0)
				       result)
				   (while (string-match "[*#:.]+" index-name index)
				     (push (cons (match-beginning 0) (match-end 0)) result)
				     (setq index (match-end 0)))
				   result))
	     found)
	(some (lambda (separator-pair)
		(let* ((begin (car separator-pair))
		       (end (cdr separator-pair))
		       (before-name (substring index-name 0 begin))
		       (after-name (substring index-name end))
		       (parent-path (concat prefix "/" before-name))
		       (parent-node (gethash parent-path hash)))
		  (when parent-node
		    (push (cons :parent parent-path) node)
		    (unless (assq :name node)
		      (push (cons :name after-name) node))
		    (puthash path node hash)
		    (unless (assq :widget parent-node)
		      (let* ((parent-element (cdr (assq :element parent-node)))
			     (parent-index-name (car parent-element))
			     (parent-index-position (if (consp (cdr parent-element)) (cadr parent-element) (cdr parent-element)))
			     (parent-function (when (consp (cdr parent-element)) (caddr parent-element)))
			     (open-status (gethash parent-path ide-skel-imenu-open-paths))
			     (parent-arguments (when (consp (cdr parent-element)) (cdddr parent-element))))
			(push (cons :widget
				    ;; internal node
				    (list 'ide-skel-imenu-internal-node-widget
					  :open open-status
					  :indent 0
					  :path parent-path
					  :notify 'ide-skel-tree-node-notify
					  :index-name parent-index-name
					  :index-position parent-index-position
					  :function parent-function
					  :arguments parent-arguments
					  :node (list 'push-button
						      :format "%[%t%]\n"
						      :button-face 'variable-pitch
						      :tag (or (cdr (assq :name parent-node))
							       before-name)
						      ;; :tag (cadr (assq :element parent-node))
						      )))
			      parent-node)
			(puthash parent-path parent-node hash)))
		    t)))
	      reverse-separators)))))

;; widget linking, leafs creation
(defun ide-skel-imenu-analyze3 (hash prefix element)
  (when element
    (if (and (consp (cdr element))
	     (listp (cadr element)))
	(dolist (elem (cdr element))
	  (ide-skel-imenu-analyze3 hash (concat prefix "/" (car element)) elem))
      (let* ((index-name (car element))
	     (index-position (if (consp (cdr element)) (cadr element) (cdr element)))
	     (function (when (consp (cdr element)) (caddr element)))
	     (arguments (when (consp (cdr element)) (cdddr element)))
	     (path (concat prefix "/" index-name))
	     (node (gethash path hash))
	     (widget (cdr (assq :widget node)))
	     (parent-path (cdr (assq :parent node)))
	     (parent-node (when parent-path (gethash parent-path hash)))
	     (parent-widget (when parent-node (cdr (assq :widget parent-node)))))
	;; create leaf if not exists
	(unless widget
	  ;; leaf node
	  (push (cons :widget (list 'ide-skel-imenu-leaf-widget
				    :notify 'ide-skel-tree-node-notify
				    :index-name index-name
				    :index-position index-position
				    :function function
				    :arguments arguments
				    :tag (or (cdr (assq :name node))
					     index-name)))
		node)
	  (puthash path node hash)
	  (setq widget (cdr (assq :widget node))))
	;; add to parent
	(when parent-widget
	  (setcdr (last parent-widget) (cons widget nil)))))))

(defun ide-skel-imenu-create-tree (hash prefix element)
  (when element 
    (if (and (consp (cdr element))
	     (listp (cadr element)))
	(let* ((menu-title (car element))
	       (sub-alist (cdr element))
	       (path (concat prefix "/" menu-title))
	       (open-status (gethash path ide-skel-imenu-open-paths)))
	  (append
	   (list 'ide-skel-imenu-internal-node-widget
		 :open open-status
		 :indent 0
		 :path path
		 :node (list 'push-button
			     :format "%[%t%]\n"
			     :button-face 'variable-pitch
			     :tag menu-title))
	   (delq nil (mapcar (lambda (elem)
			       (ide-skel-imenu-create-tree hash path elem))
			     sub-alist))))
      (let* ((index-name (car element))
	     (index-position (if (consp (cdr element)) (cadr element) (cdr element)))
	     (function (when (consp (cdr element)) (caddr element)))
	     (arguments (when (consp (cdr element)) (cdddr element)))
	     (path (concat prefix "/" index-name))
	     (node (gethash path hash))
	     (parent-path (cdr (assq :parent node)))
	     (widget (cdr (assq :widget node))))
	(unless parent-path
	  widget)))))

(defun ide-skel-imenu-compare (e1 e2)
  (let ((ce1 (and (consp (cdr e1)) (listp (cadr e1))))
	(ce2 (and (consp (cdr e2)) (listp (cadr e2)))))
    (when ce1
      (setcdr e1 (sort (cdr e1) 'ide-skel-imenu-compare)))
    (when ce2
      (setcdr e2 (sort (cdr e2) 'ide-skel-imenu-compare)))
    (if (or (and ce1 ce2)
	    (and (not ce1) (not ce2)))
	(string< (car e1) (car e2))
      (and ce1 (not ce2)))))

(defun ide-skel-outline-tree-create (index-alist)
  (let (stack
	node-list
	(current-depth 0))
    (dolist (element index-alist)
      (let ((index-name (car element))
	    (index-position (if (consp (cdr element)) (cadr element) (cdr element)))
	    (function (when (consp (cdr element)) (caddr element)))
	    (arguments (when (consp (cdr element)) (cdddr element))))
	;; (message "index-name: %S" index-name)
	(string-match "^\\([*]+\\)[ ]*\\(.*\\)$" index-name)
	(let* ((depth (length (match-string 1 index-name)))
	       (name (match-string 2 index-name))
	       parent-node
	       node)
	  (while (and stack
		      (>= (caar stack) depth))
	    (setq stack (cdr stack)))
	  (when stack
	    (setq parent-node (cdar stack))
	    (when (eq (car parent-node) 'ide-skel-imenu-leaf-widget)
	      (let ((path (plist-get (cdr parent-node) :path)))
		(setcar parent-node 'ide-skel-imenu-internal-node-widget)
		(setcdr parent-node (list :open (gethash path ide-skel-imenu-open-paths)
					  :indent 0
					  :notify 'ide-skel-tree-node-notify
					  :index-name (plist-get (cdr parent-node) :index-name)
					  :index-position (plist-get (cdr parent-node) :index-position)
					  :function (plist-get (cdr parent-node) :function)
					  :arguments (plist-get (cdr parent-node) :arguments)
					  :path path
					  :node (list 'push-button
						      :format "%[%t%]\n"
						      :button-face 'variable-pitch
						      :tag (plist-get (cdr parent-node) :tag)))))))
	  (setq node (list 'ide-skel-imenu-leaf-widget
			   :notify 'ide-skel-tree-node-notify
			   :index-name index-name
			   :index-position index-position
			   :function function
			   :path (concat (plist-get (cdr parent-node) :path) "/" index-name)
			   :arguments arguments
			   :tag name))
	  (push (cons depth node) stack)
	  (if parent-node
	      (setcdr (last parent-node) (cons node nil))
	    (push node node-list)))))
    (append
     (list 'ide-skel-imenu-internal-node-widget
	   :open t
	   :indent 0
	   :path ""
	   :tag "")
     (reverse node-list))))

(defun ide-skel-imenu-side-view-draw-tree (imenu-buffer &optional refresh)
  (with-current-buffer imenu-buffer
    (let ((index-alist (with-current-buffer ide-skel-imenu-editor-buffer
			(when refresh
			  (imenu--cleanup)
			  (setq imenu--index-alist nil))
			(cons "" (progn
				   (unless imenu--index-alist
				     (font-lock-default-fontify-buffer)
				     (condition-case err
					 (imenu--make-index-alist t)
				       (error nil)))
				   imenu--index-alist))))
	  (is-outline-mode (with-current-buffer ide-skel-imenu-editor-buffer
			     (or (eq major-mode 'outline-mode)
				 (and (boundp 'outline-minor-mode)
				      (symbol-value 'outline-minor-mode)))))
	  (inhibit-read-only t)
	  (hash (make-hash-table :test 'equal))
	  (start-line (save-excursion
			(goto-char (window-start ide-skel-current-right-view-window))
			(line-number-at-pos))))
      (unless is-outline-mode
	(when ide-skel-imenu-sorted
	  (setq index-alist (cons "" (sort (copy-tree (cdr index-alist)) 'ide-skel-imenu-compare))))
	(ide-skel-imenu-analyze hash "/" index-alist)
	(ide-skel-imenu-analyze2 hash "/" index-alist)
	(ide-skel-imenu-analyze3 hash "/" index-alist))
      (let ((tree (if is-outline-mode
		      (ide-skel-outline-tree-create (cdr index-alist))
		    (ide-skel-imenu-create-tree hash "/" index-alist))))
	(plist-put (cdr tree) :open t)
	(plist-put (cdr tree) :indent 0)
	(erase-buffer)
	(tree-widget-set-theme "small-folder")
	(widget-create tree)
	(set-keymap-parent (current-local-map) tree-widget-button-keymap)
	(widget-setup)
	(goto-line start-line)
	(beginning-of-line)
	(set-window-start ide-skel-current-right-view-window (point))))))

(defun ide-skel-imenu-side-view-window-function (side event &rest list)
  ;; (message "%S %S %S" side event list)
  (when (and (eq side 'right)
	     ide-skel-current-right-view-window)
    (let ((imenu-buffer (ide-skel-imenu-get-buffer-create ide-skel-current-editor-buffer t)))
      (when (memq event '(show editor-buffer-changed))
	(when (ide-skel-has-imenu ide-skel-current-editor-buffer)
	  (unless imenu-buffer
	    (setq imenu-buffer (ide-skel-imenu-get-buffer-create ide-skel-current-editor-buffer)))
	  (with-current-buffer imenu-buffer
	    (setq ide-skel-tabbar-enabled t))))
      (when (and imenu-buffer
		 (eq event 'tab-change)
		 (eq (cadr list) imenu-buffer))
	(with-current-buffer imenu-buffer
	  (when (= (buffer-size) 0)
	    (ide-skel-imenu-side-view-draw-tree imenu-buffer))))))
  nil)

(add-hook 'ide-skel-side-view-window-functions 'ide-skel-imenu-side-view-window-function)

;;; Info

(require 'info)

(defun ide-skel-info-get-buffer-create ()
  (let ((buffer (ide-skel-get-side-view-buffer-create " Ide Skel Info"
						      'left "Info" "Info browser"
						      (lambda (editor-buffer) t))))
    (with-current-buffer buffer
      (setq ide-skel-tabbar-menu-function
	    (lambda ()
	      (append
	       (list
		(list 'ide-skel-info-refresh "Refresh" t))))
	    ide-skel-info-open-paths (make-hash-table :test 'equal)
	    ide-skel-info-root-node (cons "Top" "(dir)top"))
      (add-hook 'tree-widget-after-toggle-functions (lambda (widget)
						      (let ((path (widget-get widget :path)))
							(when path
							  (if (widget-get widget :open)
							      (puthash path t ide-skel-info-open-paths)
							    (remhash path ide-skel-info-open-paths)))))
		nil t))
    buffer))

(defun ide-skel-info-file-open (widget &rest rest)
  (let ((path (widget-get widget :path)))
    (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" path))
	(error "Invalid node %s" path)
      (let ((filename (match-string 1 path))
	    (nodename (match-string 2 path))
	    (buffer (get-buffer "*info*"))
	    buffer-win)
	(unless buffer
	  (with-selected-window (ide-skel-get-last-selected-window)
	    (info)
	    (setq buffer (window-buffer (selected-window)))
	    (setq buffer-win (selected-window))))
	(unless buffer-win
	  (setq buffer-win (get-buffer-window buffer))
	  (unless buffer-win
	    (with-selected-window (ide-skel-get-last-selected-window)
	      (switch-to-buffer buffer)
	      (setq buffer-win (selected-window)))))
	(select-window buffer-win)
	(Info-find-node filename nodename)))))

(defun ide-skel-info-tree-expand-dir (tree)
  (let ((path (widget-get tree :path)))
    (condition-case err
	(mapcar 'ide-skel-info-tree-widget (Info-speedbar-fetch-file-nodes path))
      (error
       (message "%s" (error-message-string err))
       nil))))

(defun ide-skel-info-tree-widget (e)
  (let ((name (car e))
	(path (cdr e)))
    (if (condition-case err
	    (Info-speedbar-fetch-file-nodes path)
	  (error nil))
	(list 'ide-skel-info-tree-dir-widget
	      :path path
	      :help-echo name
	      :open (gethash path ide-skel-info-open-paths)
	      :node (list 'push-button
			  :tag name
			  :format "%[%t%]\n"
			  :notify 'ide-skel-info-file-open
			  :path path
			  :button-face 'variable-pitch
			  :help-echo name
			  :keymap tree-widget-button-keymap
			  ))
      (list 'ide-skel-info-tree-file-widget
	    :path path
	    :help-echo name
	    :keymap tree-widget-button-keymap
	    :tag name))))
	
(defun ide-skel-info-refresh (&optional show-top)
  (interactive)
  (with-current-buffer ide-skel-info-buffer
    (let ((inhibit-read-only t)
	  (start-line (save-excursion
			(goto-char (window-start ide-skel-current-left-view-window))
			(line-number-at-pos))))
      (erase-buffer)
      (tree-widget-set-theme "small-folder")
      (let ((tree (ide-skel-info-tree-widget ide-skel-info-root-node)))
	(plist-put (cdr tree) :open t)
	(widget-create tree))
      (set-keymap-parent (current-local-map) tree-widget-button-keymap)
      (widget-setup)
      (if show-top
	  (goto-char (point-min))
	(goto-line start-line))
      (beginning-of-line)
      (set-window-start ide-skel-current-right-view-window (point)))))

(defun ide-skel-info (root-node)
  (with-current-buffer ide-skel-info-buffer
    (clrhash ide-skel-info-open-paths)
    (setq ide-skel-info-root-node root-node)
    (ide-skel-info-refresh t)))

(defun ide-skel-info-side-view-window-function (side event &rest list)
  (when (and (eq side 'left) ide-skel-current-left-view-window)
    (cond ((eq event 'show)
	   (unless ide-skel-info-buffer
	     (setq ide-skel-info-buffer (ide-skel-info-get-buffer-create)))
	   (with-current-buffer ide-skel-info-buffer
	     (setq ide-skel-tabbar-enabled t)))
	  ((and (eq event 'tab-change)
		(eq (cadr list) ide-skel-info-buffer)
		(= (buffer-size ide-skel-info-buffer) 0))
	   (ide-skel-info-refresh))))
  nil)

(add-hook 'ide-skel-side-view-window-functions 'ide-skel-info-side-view-window-function)

;;; Dir tree

(defun ide-skel-dir-node-notify (widget &rest rest)
  (let ((path (widget-get widget :path)))
    (ide-skel-dir path)))

(defun ide-skel-file-open (widget &rest rest)
  (let ((path (widget-get widget :path)))
    (ide-skel-select-buffer path)))

(defun ide-skel-dir-tree-widget (e)
  "Return a widget to display file or directory E."
  (if (file-directory-p e)
      `(ide-skel-dir-tree-dir-widget
        :path ,e
	:help-echo ,e
	:open ,(gethash e ide-skel-dir-open-paths)
        :node (push-button
               :tag ,(file-name-as-directory
                      (file-name-nondirectory e))
               :format "%[%t%]\n"
               :notify ide-skel-dir-node-notify
	       :path ,e
	       :button-face (variable-pitch bold)
               :help-echo ,e
               :keymap        ,tree-widget-button-keymap  ; Emacs
               ))
    `(ide-skel-dir-tree-file-widget
      :path ,e
      :help-echo ,e
      :tag  ,(file-name-nondirectory e))))

(defun ide-skel-dir-get-buffer-create ()
  (let ((buffer (ide-skel-get-side-view-buffer-create " Ide Skel Dirs"
						      'left "Dirs" "Filesystem browser"
						      (lambda (editor-buffer) t))))
    (with-current-buffer buffer
      (setq ide-skel-tabbar-menu-function
	    (lambda ()
	      (append
	       (list
		(list 'ide-skel-dir-refresh "Refresh" t)
		(when (and (buffer-file-name ide-skel-current-editor-buffer)
			   (fboundp 'ide-skel-proj-get-project-create)
			   (funcall 'ide-skel-project-p (car (funcall 'ide-skel-proj-get-project-create (buffer-file-name ide-skel-current-editor-buffer)))))
		  (list 'ide-skel-dir-project "Show project tree" t))
		(list 'ide-skel-dir-home "Home" t)
		(list 'ide-skel-dir-filesystem-root "/" t)
		)))
	    ide-skel-dir-open-paths (make-hash-table :test 'equal)
	    ide-skel-dir-root-dir (file-truename (substitute-in-file-name "~")))
      (add-hook 'tree-widget-after-toggle-functions (lambda (widget)
						      (let ((path (widget-get widget :path)))
							(when path
							  (if (widget-get widget :open)
							      (puthash path t ide-skel-dir-open-paths)
							    (remhash path ide-skel-dir-open-paths)))))
		nil t))
    buffer))

(defun ide-skel-dir-tree-list (dir)
  "Return the content of the directory DIR.
Return the list of components found, with sub-directories at the
beginning of the list."
  (let (files dirs)
    (dolist (entry (directory-files dir 'full))
      (unless (string-equal (substring entry -1) ".")
        (if (file-directory-p entry)
            (push entry dirs)
          (push entry files))))
    (nreverse (nconc files dirs))))

(defun ide-skel-dir-tree-expand-dir (tree)
  "Expand the tree widget TREE.
Return a list of child widgets."
  (let ((dir (directory-file-name (widget-get tree :path))))
    (if (file-accessible-directory-p dir)
	(progn
	  (message "Reading directory %s..." dir)
	  (condition-case err
	      (prog1
		  (mapcar 'ide-skel-dir-tree-widget (ide-skel-dir-tree-list dir))
		(message "Reading directory %s...done" dir))
	    (error
	     (message "%s" (error-message-string err))
	     nil)))
      (error "This directory is inaccessible"))))

(defun ide-skel-select-dir-handler (event)
  (interactive "@e")
  (with-selected-window (posn-window (event-start event))
    (let* ((path (get-text-property (posn-point (event-start event)) 'path)))
      (ide-skel-dir path))))

(defun ide-skel-dir-refresh (&optional show-top)
  (interactive)
  (with-current-buffer ide-skel-dir-buffer
    (let ((inhibit-read-only t)
	  (start-line (save-excursion
			(goto-char (window-start ide-skel-current-left-view-window))
			(line-number-at-pos))))
      (erase-buffer)
      (let ((path-dirs (split-string (directory-file-name ide-skel-dir-root-dir) "[/\\]"))
	    (km (make-sparse-keymap))
	    path)
	(setq path-dirs (reverse (cdr (reverse path-dirs))))
	(define-key km [mouse-1] 'ide-skel-select-dir-handler)
	(while path-dirs
	  (let ((dir (car path-dirs)))
	    (when (and (> (current-column) 0)
		       (>= (+ (current-column) (length dir) 1) (window-width ide-skel-current-left-view-window)))
	      (insert "\n"))
	    (setq path (directory-file-name (concat path (format "/%s" dir))))
	    (unless (equal (char-before) ?/)
	      (insert "/"))
	    (insert (propertize dir
				'face 'bold
				'local-map km
				'mouse-face 'highlight
				'path path)))
	  (setq path-dirs (cdr path-dirs))))
      (insert "\n\n")
      (tree-widget-set-theme "small-folder")
      (let ((default-directory ide-skel-dir-root-dir)
	    (tree (ide-skel-dir-tree-widget (directory-file-name ide-skel-dir-root-dir))))
	(plist-put (cdr tree) :open t)
	(widget-create tree))
      (set-keymap-parent (current-local-map) tree-widget-button-keymap)
      (widget-setup)
      (if show-top
	  (goto-char (point-min))
	(goto-line start-line))
      (beginning-of-line)
      (set-window-start ide-skel-current-right-view-window (point))
      )))

(defun ide-skel-dir (root-dir)
  (with-current-buffer ide-skel-dir-buffer
    (clrhash ide-skel-dir-open-paths)
    (setq ide-skel-dir-root-dir (file-truename (substitute-in-file-name root-dir)))
    (ide-skel-dir-refresh t)))

(defun ide-skel-dir-project ()
  (interactive)
  (let ((root-dir (funcall 'ide-skel-project-root-path
			   (car (funcall 'ide-skel-proj-get-project-create (buffer-file-name ide-skel-current-editor-buffer))))))
    (message "Root dir: %S" root-dir)
    (ide-skel-dir root-dir)))

(defun ide-skel-dir-home ()
  (interactive)
  (ide-skel-dir "~"))

(defun ide-skel-dir-filesystem-root ()
  (interactive)
  (ide-skel-dir "/"))

(defun ide-skel-dirs-side-view-window-function (side event &rest list)
  (when (and (eq side 'left) ide-skel-current-left-view-window)
    (cond ((eq event 'show)
	   (unless ide-skel-dir-buffer
	     (setq ide-skel-dir-buffer (ide-skel-dir-get-buffer-create)))
	   (with-current-buffer ide-skel-dir-buffer
	     (setq ide-skel-tabbar-enabled t)))
	  ((and (eq event 'tab-change)
		(eq (cadr list) ide-skel-dir-buffer)
		(= (buffer-size ide-skel-dir-buffer) 0))
	   (ide-skel-dir-refresh))))
  nil)

(add-hook 'ide-skel-side-view-window-functions 'ide-skel-dirs-side-view-window-function)

(easy-menu-add-item nil nil ide-skel-project-menu t)

(defun ide-skel-proj-insert-with-face (string face)
  (let ((point (point)))
    (insert string)
    (let ((overlay (make-overlay point (point))))
      (overlay-put overlay 'face face))))

(defun ide-skel-mode-name-stringify (mode-name)
  (let ((name (format "%s" mode-name)))
    (replace-regexp-in-string "-" " "
			      (capitalize
			       (if (string-match "^\\(.*\\)-mode" name)
				   (match-string 1 name)
				 name)))))

(defun ide-skel-proj-get-all-dirs (root-dir)
  (condition-case err
      (split-string (shell-command-to-string (format "find %s -type d | grep -v '/CVS\\|/\\.svn'" root-dir))
		    "\n" t)
    (error nil)))

(defun ide-skel-shell ()
  (interactive)
  (when (fboundp 'ide-skel-show-bottom-view-window)
    (funcall 'ide-skel-show-bottom-view-window)
    (select-window (or (funcall 'ide-skel-get-bottom-view-window)
		       (selected-window)))
    (ansi-term (or (getenv "ESHELL") (getenv "SHELL")))))

(defun ide-skel-project-menu (menu)
  (let* ((curbuf-file (buffer-file-name (current-buffer)))
	 (curbuf-mode-name (when (and (buffer-file-name (current-buffer))
				      (ide-skel-mode-file-regexp-list (list major-mode)))
			     (ide-skel-mode-name-stringify major-mode))))
    (condition-case err
	(append
	 (when curbuf-mode-name
	   (list (vector (format "Search for %s file..." curbuf-mode-name) 'ide-skel-proj-find-files-by-regexp curbuf-mode-name)))
	 (list (vector "Search for file..." 'ide-skel-proj-find-text-files-by-regexp curbuf-mode-name))
	 (when curbuf-mode-name
	   (list (vector (format "Grep %s files..." curbuf-mode-name) 'ide-skel-proj-grep-files-by-regexp curbuf-mode-name)))
	 (list (vector "Grep files..." 'ide-skel-proj-grep-text-files-by-regexp curbuf-file))
	 (list (vector "Shell" 'ide-skel-shell t)))
      (error (message (error-message-string err))))))

;; (ide-skel-project . relative-path) jesli path nalezy do projektu,
;; (qdir . filename) wpp

(defun ide-skel-proj-get-project-create (path)
  (let ((path (file-truename (substitute-in-file-name path)))
	dir)
    (if (file-directory-p path)
	(progn
	  (setq path (file-name-as-directory path))
	  (setq dir path))
      (setq dir (file-name-as-directory (file-name-directory path))))
    ;; path - true, qualified file name (no environment variables, ~, links)
    (let ((project (some (lambda (project)
			   (let ((root-dir (ide-skel-project-root-path project)))
			     (when (string-match (concat "^" (regexp-quote root-dir)) path)
			       project)))
			 ide-skel-projects)))
      (when project
	(setq dir (ide-skel-project-root-path project)))
      ;; there is no such project
      (unless project
	(let ((last-project-dir dir)
	      (dir-list (split-string dir "/"))
	      is-project)
	  ;; there is no root dir
	  (while (directory-files dir t (concat "^" ide-skel-cvs-dir-regexp) t)
	    (setq is-project t
		  last-project-dir (file-name-as-directory dir)
		  dir (file-name-as-directory (file-name-directory (directory-file-name dir)))))
	  (when is-project
	    (let ((list (nthcdr (1- (length (split-string last-project-dir "/"))) dir-list)))
	      (cond ((equal (car list) "trunk")
		     (setq last-project-dir (concat last-project-dir "trunk/")))
		    ((member (car list) '("branches" "tags"))
		     (setq last-project-dir (concat last-project-dir (car list) "/" (when (cdr list) (concat (cadr list) "/")))))
		    (t)))
	    (setq project (make-ide-skel-project :root-path last-project-dir
						 :include-file-path (ide-skel-proj-get-all-dirs last-project-dir))
		  dir last-project-dir)
	    (push project ide-skel-projects))))
      (list (or project dir) (file-relative-name path dir) path))))

(defun ide-skel-proj-get-root (proj-or-dir)
  (when proj-or-dir
    (directory-file-name (file-truename (substitute-in-file-name 
					 (if (ide-skel-project-p proj-or-dir)
					     (ide-skel-project-root-path proj-or-dir)
					   proj-or-dir))))))
	  
(defun ide-skel-proj-find-files (dir file-predicate &optional dir-predicate)
  "Return list of all qualified file paths in tree dir with root
DIR, for which FILE-PREDICATE returns non-nil.  We will go into
directory only if DIR-PREDICATE returns non-nil or DIR-PREDICATE *is* nil."
  (setq dir (file-name-as-directory (file-truename (substitute-in-file-name dir))))
  (let (result-list)
    (mapcar (lambda (path)
	      (if (file-directory-p path)
		  (when (and (file-accessible-directory-p path)
			     (or (null dir-predicate)
				 (funcall dir-predicate path)))
		    (setq result-list (append result-list (ide-skel-proj-find-files path file-predicate dir-predicate))))
		(when (or (null file-predicate)
			  (funcall file-predicate path))
		  (push path result-list))))
	    (delete (concat (file-name-as-directory dir) ".") 
		    (delete (concat (file-name-as-directory dir) "..")
			    (directory-files dir t nil t))))
    result-list))

(defun ide-skel-root-dir-for-path (path)
  (let (root-dir)
    (setq root-dir (car (ide-skel-proj-get-project-create path)))
    (unless (stringp root-dir)
      (setq root-dir (ide-skel-project-root-path root-dir)))
    root-dir))

(defun ide-skel-has-imenu (&optional buffer)
  (with-current-buffer (or buffer (current-buffer))
    (or (and imenu-prev-index-position-function
	     imenu-extract-index-name-function)
	imenu-generic-expression
	(not (eq imenu-create-index-function
		 'imenu-default-create-index-function)))))

(defun ide-skel-mode-file-regexp-list (mode-symbol-list)
  (delq nil (mapcar (lambda (element)
		      (let ((fun-name (if (listp (cdr element)) (cadr element) (cdr element))))
			(when (memq fun-name mode-symbol-list) (cons (car element) fun-name))))
		    auto-mode-alist)))

(defun ide-skel-find-project-files (root-dir mode-symbol-list predicate)
  (let ((obj-file-regexp-list (delq nil (mapcar (lambda (element)
						  (let ((len (length element)))
						    (unless (and (> len 0)
								 (equal (elt element (1- len)) ?/))
						      (concat (regexp-quote element) "$"))))
						(append ide-skel-proj-ignored-extensions completion-ignored-extensions))))
	(mode-file-regexp-list (ide-skel-mode-file-regexp-list mode-symbol-list))) ; (file-path-regexp . major-mode-function-symbol)
    (when (and mode-symbol-list
	       (not mode-file-regexp-list))
      (error (format "No rules for %s major modes in auto-mode-alist." (mapconcat 'identity mode-symbol-list ", "))))
    (ide-skel-proj-find-files root-dir
			      (lambda (file-name)
				(and (not (string-match "#" file-name))
				     (not (string-match "semantic.cache" file-name))
				     (or (and (not mode-symbol-list)
					      (not (some (lambda (regexp)
								  (string-match regexp file-name))
								obj-file-regexp-list)))
					 (and mode-symbol-list
					      (some (lambda (element)
						      (let ((freg (if (string-match "[$]" (car element))
								      (car element)
								    (concat (car element) "$"))))
							(when (string-match freg file-name)
							  (cdr element))))
						    mode-file-regexp-list)))
				     (or (not predicate)
					 (funcall predicate file-name))))
			      (lambda (dir-path)
				(not (string-match (concat "/" ide-skel-cvs-dir-regexp) dir-path))))))

(defun ide-skel-proj-find-text-files-by-regexp ()
  (interactive)
  (unwind-protect
      (progn
	(setq ide-skel-all-text-files-flag t)
	(call-interactively 'ide-skel-proj-find-files-by-regexp))
    (setq ide-skel-all-text-files-flag nil)))

(defun ide-skel-proj-grep-text-files-by-regexp ()
  (interactive)
  (unwind-protect
      (progn
	(setq ide-skel-all-text-files-flag t)
	(call-interactively 'ide-skel-proj-grep-files-by-regexp))
    (setq ide-skel-all-text-files-flag nil)))

(defun ide-skel-proj-grep-files-by-regexp (root-dir mode-symbol-list regexp)
  (interactive (let* ((path (buffer-file-name (current-buffer)))
		      (all-text-files (or ide-skel-all-text-files-flag
					  (consp current-prefix-arg)))
		      (whatever (progn
				  (when (and (not all-text-files)
					     (not (ide-skel-mode-file-regexp-list (list major-mode))))
				    (error (format "No rules for %s major mode in auto-mode-alist" (symbol-name major-mode))))
				  (unless path
				    (error "Current buffer (%s) is not visiting any project file" (buffer-name (current-buffer))))))
		      (root-dir (when path (ide-skel-root-dir-for-path path)))
		      (thing (let ((res (thing-at-point 'symbol)))
			       (set-text-properties 0 (length res) nil res)
			       res))
		      (chunk (let ((result (read-string (concat (if root-dir (format "Root dir is %s. " root-dir) "")
								(format "Search in %s files. Regexp%s: "
									(if all-text-files
									    "all text"
									  (ide-skel-mode-name-stringify major-mode))
									(if thing (format " (default %s)" thing) "")))
							nil ide-skel-proj-grep-project-files-history thing)))
			       (if (and result (> (length result) 0))
				   result
				 (error "Regexp cannot be null")))))
		 (list root-dir (unless all-text-files (list major-mode)) chunk)))
  (let* ((paths (ide-skel-find-project-files root-dir mode-symbol-list (lambda (path) t)))
	 (temp-file-path (concat (file-name-as-directory temporary-file-directory) (make-temp-name "ide-"))))
    (unless paths
      (error "No files to grep"))
    ;; create temporary file with file paths to search
    (with-temp-file temp-file-path
      (dolist (path paths)
	;; save buffer if is open
	(let ((buffer (get-file-buffer path)))
	  (when (and buffer
		     (buffer-live-p buffer))
	    (with-current-buffer buffer
	      (save-buffer))))
	(setq path (concat "./" (file-relative-name path (file-name-as-directory root-dir))))
	(insert (concat "'" path "'\n"))))
    (let* ((default-directory root-dir)
	   (grep-command (format "cat %s | xargs grep -n %s" temp-file-path regexp)))
      (setq ide-skel-proj-grep-header (list root-dir
					    (if mode-symbol-list
						(mapconcat (lambda (sym) (ide-skel-mode-name-stringify sym)) mode-symbol-list ", ")
					      "all text")
					    regexp))
      (grep grep-command))
    ;; delete file after some time, because grep is executed as external process
    (run-with-idle-timer 5 nil (lambda (file-path)
				 (condition-case nil
				     nil ; (delete-file file-path)
				   (error nil)))
			 temp-file-path)))

(defun ide-skel-proj-find-files-by-regexp (root-dir mode-symbol-list name-regexp &optional case-sensitive)
  "Search directory tree with root in ROOT-DIR and returns
qualified paths to files which after open in Emacs would have one
of modes in MODE-SYMBOL-LIST (if list is empty, we will take all
text files) and their name (without dir) matches NAME-REGEXP."
  (interactive (let* ((path (buffer-file-name (current-buffer)))
		      (all-text-files (or ide-skel-all-text-files-flag
					  (consp current-prefix-arg)))
		      (whatever (progn
				  (when (and (not all-text-files)
					     (not (ide-skel-mode-file-regexp-list (list major-mode))))
				    (error (format "No rules for %s major mode in auto-mode-alist" (symbol-name major-mode))))
				  (unless path
				    (error "Current buffer (%s) is not visiting any project file" (buffer-name (current-buffer))))))
		      (root-dir (when path (ide-skel-root-dir-for-path path)))
		      (chunk (read-string (concat (if root-dir (format "Root dir is %s. " root-dir) "")
						  (if all-text-files
						      "F"
						    (concat (ide-skel-mode-name-stringify major-mode) " f"))
						  (format "ile name regexp: " ))
					  nil ide-skel-proj-find-project-files-history nil)))
		 (list root-dir (unless all-text-files (list major-mode)) chunk)))
  (let* ((paths (ide-skel-find-project-files root-dir mode-symbol-list
					    (lambda (path)
					      (let ((case-fold-search (not case-sensitive)))
						(or (not name-regexp)
						    (string-match name-regexp (file-name-nondirectory path)))))))
	 (buffer (get-buffer-create ide-skel-proj-find-results-buffer-name))
	 (saved-window (cons (selected-window) (window-buffer (selected-window)))))
    (if (= (length paths) 1)
	(find-file (car paths))
      (save-selected-window
	(save-excursion
	  (set-buffer buffer)
	  (setq buffer-read-only nil
		default-directory root-dir)
	  (erase-buffer)

	  (insert "Root dir: ")
	  (ide-skel-proj-insert-with-face root-dir 'font-lock-keyword-face)
	  (insert "; Range: ")
	  (ide-skel-proj-insert-with-face
	   (if mode-symbol-list
	       (mapconcat (lambda (sym) (ide-skel-mode-name-stringify sym)) mode-symbol-list ", ")
	     "all text")
	   'font-lock-keyword-face)
	  (insert " files; Regexp: ")
	  (ide-skel-proj-insert-with-face name-regexp 'font-lock-keyword-face)
	  (insert "; Case sensitive: ")
	  (ide-skel-proj-insert-with-face (if case-sensitive "Yes" "No") 'font-lock-keyword-face)
	  (insert "\n\n")
	  (compilation-minor-mode 1)
	  (let ((invisible-suffix ":1:1 s"))
	    (put-text-property 0 (length invisible-suffix) 'invisible t invisible-suffix)
	    (dolist (path paths)
	      (let ((relative-path (file-relative-name path root-dir)))
		(put-text-property 0 (length relative-path) 'mouse-face 'highlight relative-path)
		(insert relative-path)
		(insert invisible-suffix)
		(insert "\n"))))
	  (insert (format "\n%d files found." (length paths)))
	  (goto-char (point-min))
	  (setq buffer-read-only t)
	  (when (and paths (fboundp 'compile-reinitialize-errors) (funcall (symbol-function 'compile-reinitialize-errors) t)))
	  (switch-to-buffer-other-window buffer)
	  (goto-line 1)
	  (goto-line 3)))
      (if (window-live-p (car saved-window))
	  (select-window (car saved-window))
	(when (get-buffer-window (cdr saved-window))
	  (select-window (get-buffer-window (cdr saved-window))))))))

(unless ide-skel-proj-grep-mode-map
  (setq ide-skel-proj-grep-mode-map (make-sparse-keymap))
  (define-key ide-skel-proj-grep-mode-map "r" 'ide-skel-proj-grep-replace))

(defun ide-skel-proj-grep-replace ()
  (interactive)
  (let ((replace-to (read-string "Replace to: " nil 'ide-skel-proj-grep-replace-history))
	(current-pos 1)
	begin end
	buffers-to-revert
	replace-info)
    (save-excursion
      (while current-pos
	(setq current-pos (next-single-property-change current-pos 'font-lock-face (current-buffer)))
	(when (and current-pos
		   (eq (get-text-property current-pos 'font-lock-face) 'match))
	  (setq begin current-pos)
	  (setq current-pos (next-single-property-change current-pos 'font-lock-face (current-buffer)))
	  (setq end current-pos)
	  (save-excursion
	    (goto-char begin)
	    (beginning-of-line)
	    (let ((begline (point)))
	      (re-search-forward "^\\(.*\\):\\([0-9]+\\):" nil t)
	      (let ((len (length (match-string 0)))
		    (file-path (expand-file-name (substring-no-properties (match-string 1)) default-directory)))
		(when (get-file-buffer file-path)
		  (push (get-file-buffer file-path) buffers-to-revert))
		(push (list file-path
			    (string-to-number (match-string 2))
			    (- begin begline len)
			    (- end begline len))
		      replace-info)))))))
    (dolist (replacement replace-info)
      (let ((file-path (nth 0 replacement))
	    (line-no (nth 1 replacement))
	    (from-column-no (nth 2 replacement))
	    (to-column-no (nth 3 replacement)))
	(condition-case err
	    (with-temp-file file-path
	      (insert-file-contents file-path)
	      (goto-line line-no)
	      (forward-char from-column-no)
	      (delete-region (point) (+ (point) (- to-column-no from-column-no)))
	      (insert replace-to))
	  (error (message "%s" (error-message-string err))))))
    (dolist (buffer buffers-to-revert)
      (when (buffer-live-p buffer)
	(with-current-buffer buffer
	  (revert-buffer t t t)))) ; ignore-auto, nonconfirm, preserve-modes
    (message "Done.")))

(define-minor-mode ide-skel-proj-grep-mode
  ""
  nil ; init value
  nil ; mode indicator
  ide-skel-proj-grep-mode-map ; keymap
  ;; body
  (unless (assq 'ide-skel-proj-grep-mode minor-mode-map-alist)
    (push (cons 'ide-skel-proj-grep-mode ide-skel-proj-grep-mode-map) minor-mode-map-alist)))

(add-hook 'grep-setup-hook (lambda ()
			     (when ide-skel-proj-grep-header
			       (ide-skel-proj-grep-mode 1)
			       (unwind-protect
				   (progn
				     (setq buffer-read-only nil)
				     (erase-buffer)
				     (remove-overlays)
				     (insert "Root dir: ")
				     (ide-skel-proj-insert-with-face (car ide-skel-proj-grep-header) 'font-lock-keyword-face)
				     (insert "; Range: ")
				     (ide-skel-proj-insert-with-face (cadr ide-skel-proj-grep-header) 'font-lock-keyword-face)
				     (insert " files; Regexp: ")
				     (ide-skel-proj-insert-with-face (caddr ide-skel-proj-grep-header) 'font-lock-keyword-face)
				     (insert "\n")
				     (insert "mouse-1 toggle match; r replace matches")
				     (insert "\n\n"))
				 (setq buffer-read-only t
				       ide-skel-proj-grep-header nil)
				 (setq ide-skel-proj-old-compilation-exit-message-function (symbol-value 'compilation-exit-message-function))
				 (set 'compilation-exit-message-function
				       (lambda (status code msg)
					 (let ((result (if ide-skel-proj-old-compilation-exit-message-function
							   (funcall ide-skel-proj-old-compilation-exit-message-function
								    status code msg)
							 (cons msg code))))
					   (save-excursion
					     (goto-char (point-min))
					     (let (begin
						   end
						   (km (make-sparse-keymap))
						   (inhibit-read-only t))
					       (define-key km [down-mouse-1] 'ignore)
					       (define-key km [mouse-1] 'ide-skel-proj-grep-click)
					       (while (setq begin (next-single-property-change (point) 'font-lock-face (current-buffer) nil))
						 (setq end (next-single-property-change begin 'font-lock-face (current-buffer) nil))
						 (put-text-property begin end 'pointer 'hand)
						 (put-text-property begin end 'local-map km)
						 (goto-char end))))
					   result)))))))

(defun ide-skel-proj-grep-click (event)
  (interactive "@e")
  (with-selected-window (posn-window (event-start event))
    (let* ((posn-point (posn-point (event-start event)))
	   (begin (or (and (not (get-text-property (1- posn-point) 'font-lock-face))
			   posn-point)
		      (previous-single-property-change posn-point 'font-lock-face (current-buffer) nil)))
	   (end (next-single-property-change posn-point 'font-lock-face (current-buffer) nil))
	   (font-lock-face (get-text-property posn-point 'font-lock-face))
	   (inhibit-read-only t))
      (put-text-property begin end 'font-lock-face (if (eq font-lock-face 'match) 'widget-field 'match)))))

(defun ide-skel-proj-change-buffer-hook-function ()
  (let ((path (buffer-file-name)))
    (when path
      (condition-case err
	  (let ((project-list (ide-skel-proj-get-project-create path)))
	    (when (ide-skel-project-p (car project-list))
	      (setq PC-include-file-path (ide-skel-project-include-file-path (car project-list)))))
	(error nil)))))

(add-hook 'ide-skel-editor-buffer-changed-hook 'ide-skel-proj-change-buffer-hook-function)

(tabbar-mode 1)

(provide 'ide-skel)

