;; -*- Mode: Emacs-Lisp -*-
;; -*- lisp -*-
;; $Id: .emacs-d-project,v 1.3 2002/07/01 12:50:07 amos-cvs Exp $
;; Copyright (C) Jan Borsodi 1998-2001
;;
;; Default project functions

(defvar project-current-project nil
  "Current project selected")
(defvar project-select-project-history nil
  "Project selection history, used by `project-load-project'")

;; Add project-select-project-history to save history list
(eval-after-load
    "save-history"
  '(setq save-history-varlist (append save-history-varlist '(project-select-project-history))))

(global-set-key [(meta f6)] 'project-load-project)

(defun project-add-include-classes (classnames classinclude)
  "Adds an object-include connection to the projects list"
  (let ()
    (setq project-include-classes (cons (list classnames classinclude) project-include-classes))))

(defun project-add-include-list (classes)
  "Adds a list of object-include to the projects list"
  (let ((inc-classes classes)
	(class))
    (while inc-classes
      (setq class (car inc-classes))
      (project-add-include-classes (car class) (car (cdr class)))
      (setq inc-classes (cdr inc-classes)))))

;; Returns the end of the include area, finds the end of the top comment and adds a newline if no includes
;; are present.
(defun end-of-include-place()
  "Finds the end of the includes, or the end of the top comments if no includes are present."
  (let ((pos))
    (save-excursion
      (beginning-of-buffer)
      (let ((count 0))
	(while (search-forward-regexp "^#include[ \t]+[\"<][a-zA-Z0-9\.\-\_]+[\">][ \t]*\n" nil t)
	  (setq count (1+ count)))
	(if (< count 1)
	    (let ()
	      (if (string-match c++-header-ext-regexp (buffer-name))
		  (let (name)
		    (setq name (concat "#ifndef[ \t]+"
;;				       (upcase (file-name-sans-extension (buffer-name)))
				       "[^ ^\t^\n]*"
;;				       "_"
;;				       (upcase (file-name-extension (buffer-name)))
				       "[ \t]*\n"
				       "#define[ \t]+"
;;				       (upcase (file-name-sans-extension (buffer-name)))
				       "[^ ^\t^\n]*"
;;				       "_"
;;				       (upcase (file-name-extension (buffer-name)))
				       "[ \t]*\n"))
;;		    (message name)
		    (search-forward-regexp name nil t))
		(let ()
		  (beginning-of-buffer)
		  (search-forward-regexp "\\(\\(\\(//[^\n]*\n\\)\\|\\(/\\*[^\\*]*\\*/[^\n]*\n\\)\\)*\\)[ \t]*\n")
		  (goto-char (match-end 1))))
	      (insert-string "\n"))))
      (setq pos (point)))
    pos))

;; Checks for known classes and adds includes on the top if none are present
;(defun insert-include( buffer buf )
(defun insert-include()
  "Insert #include on the top of the file if certain class names are found in the file"
  (interactive)
  (if (string-equal mode-name "C++")
      (let ((includes project-include-classes)
	    (include)
	    (include-classes)
	    (include-class)
	    (include-file)
	    (class-exists nil))
	(while includes
	  (setq include (car includes))
	  (setq include-classes (car include))
	  (setq include-file (car (cdr include)))
	  (setq class-exists nil)
	  (while (and (not class-exists) include-classes)
	    (setq include-class (car include-classes))
	    (save-excursion
	      (beginning-of-buffer)
	      (if (search-forward-regexp (concat "\\<" include-class "\\>") nil t)
		  (setq class-exists t)))
	    (setq include-classes (cdr include-classes)))
	  (if class-exists
	      (let ((already-present nil))
		(save-excursion
		  (beginning-of-buffer)
		  (if (search-forward-regexp (concat "^#include[ \t]+"
						     include-file
						     "[ \t]*\n") nil t )
		      (setq already-present t)))
		(if (not already-present)
		    (save-excursion
		      (goto-char (end-of-include-place))
		      (insert-string (concat "#include " include-file "\n"))))))
	  (setq includes (cdr includes))))))

(defun project-looking-at-include()
  (save-excursion
    (let ((ok nil))
      (beginning-of-line)
      (if (looking-at project-c++-include-regexp)
	  (setq ok t))
      ok)))

(defun project-looking-at-forward-class-decl()
  (save-excursion
    (let ((ok nil))
      (beginning-of-line)
      (if (looking-at project-c++-class-decl-regexp)
	  (setq ok t))
      ok)))

(defun project-find-include( class )
  (let ((classes project-include-classes)
	class-include
	class-list
	class-name
	include
	(done nil))
    (while (and classes (not done))
      (setq class-include (car classes))
      (setq class-list (car class-include))
      (message (cadr class-list))
      (while (and class-list (not done))
	(setq class-name (car class-list))
	(if (string-equal class-name class)
	    (setq done t
		  include (cadr class-include)))
	(setq class-list (cdr class-list)))
      (setq classes (cdr classes)))
    include))

;; Fix here
(defun project-try-open-include( include )
  (let (dir
	filename
	include-name
	class-name)
    (if (string-match "\"\\([^\"]*\\)\"" include)
	(let ()
	  (setq include-name (substring include (match-beginning 1) (match-end 1)))
	  (setq class-name (project-try-open-local-include include-name))
	  )
      (if (string-match "<\\([^>]*\\)>" include)
	  (let ()
	    (setq include-name (substring include (match-beginning 1) (match-end 1)))
	    (setq class-name (project-try-open-global-include include-name))
	    )
	))
    class-name))

(defun project-try-open-local-include( include-name )
  (let ((project (project-main))
	proj-dir
	inc-file
	classes
	class)
    (setq proj-dir (file-name-directory (buffer-file-name project)))
    (setq inc-file (concat proj-dir include-name))
    (setq classes (check-file inc-file))
    (if classes
	(setq class (car classes)))
    class))

(defun project-parse-tmake-line( var buf )
  (save-excursion
    (let (elements)
      (set-buffer buf)
      (beginning-of-buffer)
      (while (re-search-forward (concat "\\(.+:\\)?"
					var
					"[ \t]+[+*/-]?=\\([ \t]*[A-Za-z0-9/._]+\\)*") nil t)
	(setq elements (nconc elements (split-string (match-string-no-properties 2)))))
      elements)))

; (project-parse-tmake-line "SUBDIRS" (project-main))
; (project-main)

(defun project-get-lines (var &optional platform)
  (save-excursion
    (let ((buf (project-main))
	  (plat (cond
		 ((stringp platform)
		  (cond ((string-equal platform "")
			 "\\(\\(\\)\\)")
			(t (concat "\\(\\(" platform "\\):\\)"))))
		 ((null platform)
		  "\\(\\([a-zA-Z0-9._-]+\\):\\)?")))
	  lst)
      (set-buffer buf)
      (beginning-of-buffer)
      (while (re-search-forward (concat "^" plat
					"\\(" var "\\)"
					"[ \t]*\\([+*/-]\\)?=[ \t]*") nil t)
	(setq lst (nconc lst (list (list (match-string-no-properties 2)
					 (match-string-no-properties 3)
					 (match-string-no-properties 4))))))
      lst)))

; (project-get-lines "CONFIG" "")

(defun project-get-line (var &optional platform sign project)
  (save-excursion
    (let ((buf (or project (project-main)))
	  (plat (cond
		 ((stringp platform)
		  (cond ((string-equal platform "")
			 "")
			(t platform)))
		 (t
		  "")))
	  (sign-str (cond
		     ((stringp sign) sign)
		     (t "")))
	  str
	  ret)
      (set-buffer buf)
      (beginning-of-buffer)
      (if (re-search-forward (concat "^" plat var
				     "[ \t]*" sign-str "=[ \t]*")
			     nil t)
	  (let ((start (point))
		(end (point)))
 	    (end-of-line-nomark)
	    (backward-char-nomark)
	    (while (looking-at "\\\\[ \t]*$")
	      (setq str (concat str " " (buffer-substring-no-properties end (1- (point)))))
	      (forward-line 1)
	      (beginning-of-line-nomark)
	      (setq end (point))
 	      (end-of-line-nomark)
	      (backward-char-nomark)
	      )
	    (end-of-line-nomark)
	    (setq str (concat str " " (buffer-substring-no-properties end (point))))
	    (setq end (point))
	    (setq ret (list start end str))))
      ret)))

; (project-get-line "CONFIG")

; (mapconcat 'eval
; 	   '("a" "b")
; 	   " \\\n")


(defun project-set-line (start end items &optional breakline project)
  (save-excursion
    (let ((buf (or project (project-main)))
	  sep text tab)
      (if (stringp items)
	  (setq items (split-string items)))
      (set-buffer buf)
      (goto-char start)
      (setq tab (- (point) (line-beginning-position)))
      (setq sep (or (and breakline
			 (concat " \\\n" (make-string tab ?\ )))
		    " "))
      (setq text (mapconcat 'eval items sep))
      (kill-region start end)
      (insert text)
      text)))



; (project-get-line "CONFIG")

; (project-set-line 284 300 '("qt" "test" "blah") t)

			   

; (split-string (nth 2 (project-get-line "SOURCES")))

; (sort (split-string (project-get-line "SOURCES")) 'string<)

(defun project-find-include-paths( buf )
  (let (paths
	path
	(real-paths nil)
	proj-dir)
    (setq proj-dir (file-name-directory (buffer-file-name buf)))
    (setq paths (project-parse-tmake-line "TMAKE_INCDIR_QT" buf))
    (while paths
      (setq path (car paths))
      (setq real-paths (nconc real-paths (list (concat proj-dir path))))
      (setq paths (cdr paths)))
    real-paths))

(defun project-try-open-global-include( include-name )
  (let ((project (project-main))
	proj-dir
	inc-file
	classes
	(class nil)
	(paths project-include-paths)
	path)
    (setq proj-dir (file-name-directory (buffer-file-name project)))
    (setq paths (nconc paths (project-find-include-paths project)))
    (while (and paths (not class))
      (setq path (car paths))
      (setq path (substitute-in-file-name path))
      (setq inc-file (concat path "/" include-name))
      (if (file-exists-p inc-file)
	  (let ()
	    (setq classes (check-file inc-file))
	    (if classes
		(setq class (car classes)))))
      (setq paths (cdr paths)))
    class))

(defun project-find-class-in-include( include )
  (save-excursion
    (let (class-name)
      (setq class-name (project-find-class-in-classes include))
      (if (not class-name)
	  (setq class-name (project-try-open-include include)))
      class-name)))

(defun project-find-class-in-classes( include )
  (let ((classes project-include-classes)
	class-list
	class-names
	class-name
	class-include
	(done nil))
    (while (and classes (not done))
      (setq class-list (car classes))
      (setq class-include (cadr class-list))
      (if (string-equal class-include include)
	  (setq class-names (car class-list)
		done t))
      (setq classes (cdr classes)))
    (if done
	(setq class-name (car class-names)))
    class-name))

(defun project-convert-include()
  (save-excursion
    (let (include
	  class-name)
      (beginning-of-line)
      (if (looking-at project-c++-include-regexp)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (setq include (match-string 1))
	    (setq class-name (project-find-class-in-include include))
	    (if class-name
		(if (re-search-forward ".*\n")
		    (replace-match (concat "class " class-name ";\n"))
	      (message (concat "No class found for include " include)))))
	(if (looking-at project-c++-class-decl-regexp)
	    (save-restriction
	      (narrow-to-region (match-beginning 0) (match-end 0))
	      (setq class-name (match-string 1))
	      (setq include (project-find-include class-name))
	      (if include
		  (replace-match (concat "#include " include "\n"))
		(message (concat "Nothing known about " class-name))))
	  (message "Not a forward class declaration or include file"))))))

;;
(defun project-insert-params( class header body )
  "Insert params to a given class"
;  (interactive)
  (save-excursion
    (let ((includes project-include-params)
	  (include)
	  (include-classes)
	  (include-class)
	  (include-params-header)
	  (include-params-body)
	  (include-params)
	  (done nil)
	  (class-exists nil))
      (while (and includes (not done))
	(setq include (car includes))
	(setq include-classes (car include))
	(setq include-params-header (car (cdr include)))
	(setq include-params-body (car (cddr include)))
	(setq include-params (car (cdddr include)))
	(if (string-match include-classes class)
	    (save-excursion
	      (set-buffer header)
	      (save-excursion
		(beginning-of-buffer)
		(while (search-forward project-params-match nil t)
		  (save-restriction
		    (narrow-to-region (match-beginning 0) (match-end 0))
		    (replace-match (concat " " include-params-header " ")))))
	      (set-buffer body)
	      (save-excursion
		(beginning-of-buffer)
		(while (search-forward project-params-match nil t)
		  (save-restriction
		    (narrow-to-region (match-beginning 0) (match-end 0))
		    (replace-match (concat " " include-params-body " ")))))
	      (save-excursion
		(beginning-of-buffer)
		(while (search-forward project-params-init-match nil t)
		  (save-restriction
		    (narrow-to-region (match-beginning 0) (match-end 0))
		    (replace-match (concat " " include-params " ")))))
	      (setq done t)
	      ))
	(setq includes (cdr includes)))
      (if (not done)
	  (save-excursion
	    (set-buffer header)
	    (save-excursion
	      (beginning-of-buffer)
	      (while (search-forward project-params-match nil t)
		(save-restriction
		  (narrow-to-region (match-beginning 0) (match-end 0))
		  (replace-match ""))))
	    (set-buffer body)
	    (save-excursion
	      (beginning-of-buffer)
	      (while (search-forward project-params-match nil t)
		(save-restriction
		  (narrow-to-region (match-beginning 0) (match-end 0))
		  (replace-match ""))))
	    (save-excursion
	      (beginning-of-buffer)
	      (while (search-forward project-params-init-match nil t)
		(save-restriction
		  (narrow-to-region (match-beginning 0) (match-end 0))
		  (replace-match ""))))))
      )))


(defun check-file( file )
  "Returns a list of C++ classes found in the file FILE,
the file is expected to be C++ header file.
The returned form is:
\(CLASS1 CLASS2 ...\)

See also: `check-file-string'"
  (let (buf
	lst)
    (setq buf (generate-new-buffer "class-tmp"))
    (save-excursion
      (set-buffer buf)
      (insert-file-contents-literally file)
      (while (search-forward-regexp (concat "class[ \t\n]+\\([a-zA-Z][a-zA-Z0-9_]*[ \t\n]+\\)?\\([a-zA-Z][a-zA-Z0-9_]*\\)[ \t\n]*"
					    "\\(:[^{]*\\)?{") nil t)
	(setq lst (nconc lst (list (match-string 2))))))
    (kill-buffer buf)
    lst))


(defun scan-directory( dir local )
  "Scans the directory DIR for C++ header files,
for each file found a list of classes and a name useable for #includes is created.
A list containing all these elements are returned.
Header files ends with h, hh, H, hpp or h++
The returned format is:
\(FILE1 FILE2 ...\)
where FILE is a structure consisting of:
\(\(CLASSES\) INCLUDE\)
where CLASSES is the same structure as returned by `check-file'
and INCLUDE is a string containing either a relative filename
or a global filename used in #include.

Example 1 by running \(scan-directory \"my/src/dir\" t\)
\(\(\(MyString\) \"\\\"mystring.h\\\"\"\)\)
Example 2 by running \(scan-directory \(substitute-in-file-name \"$QTDIR/include\"\)x nil\)
\(\(\(QString\) \"<qstring.h>\"\)\)

See also: `scan-directory-string'"
  (let (files
	file
	filename
	classes
	lst)
    (setq files (directory-files dir nil ".\*\.\\(h\\|hh\\|H\\|hpp\\|h++\\)$"))
    (while files
      (setq file (car files))
      (setq classes (check-file (concat dir "/" file)))
      (if local
	  (setq filename (concat "\"" file "\""))
	(setq filename (concat "<" file ">")))
      (if classes
	  (setq lst (nconc lst (list (list classes filename)))))
      (setq files (cdr files)))
    lst))

(defun check-file-string( file )
  "Similar to `check-file' but returns a string instead,
this string is used for creating an auto-include file."
  (let (buf
	lst
	str)
    (setq buf (generate-new-buffer "class-tmp"))
    (save-excursion
      (set-buffer buf)
      (insert-file-contents-literally file)
      (while (search-forward-regexp (concat "class[ \t\n]+\\([a-zA-Z][a-zA-Z0-9_]*[ \t\n]+\\)?\\([a-zA-Z][a-zA-Z0-9_]*\\)[ \t\n]*"
					    "\\(:[^{]*\\)?{") nil t)
	(setq lst (nconc lst (list (match-string 2))))))
    (if lst
	(let* ((lsts lst)
	       cur)
	  (while lsts
	    (setq cur (car lsts))
	    (setq str (concat str " \"" cur "\""))
	    (setq lsts (cdr lsts)))
	  (setq str (concat "(" str ")")))
      (setq str nil))
    (kill-buffer buf)
    str))


(defun scan-directory-string( dir local name )
  "Similar to `scan-directory' but returns a string with a defvar statement,
the string is used for creating auto-include files."
  (let (files
	file
	filename
	classes
	(lst ""))
    (setq files (directory-files dir nil ".\*\.\\(h\\|hh\\|H\\|hpp\\|h++\\)"))
    (while files
      (setq file (car files))
      (setq classes (check-file-string (concat dir "/" file)))
      (if local
	  (setq filename (concat "\\\"" file "\\\""))
	(setq filename (concat "<" file ">")))
      (if classes
	  (setq lst (concat lst "\n\t(" classes " \"" filename "\")")))
      (setq files (cdr files)))
    (setq lst (concat "(defvar " name " '(" lst "))\n"))
    (setq lst (concat lst "\n" "(project-add-include-list " name ")\n"))
    lst))

;; List of classes known
;; (REGEXP LOWCASE LOCAL RECURSIVE CHECKFILE)
;; (REGEXP LOWCASE nil INCLUDEFILE)
(defvar project-classes
  '(
    ("^eZ[a-zA-Z0-9_]+" (check-local-class ez-class-list))
    ("^Q[a-zA-Z0-9_]+" (check-local-class qt-class-list))
    ))

(defvar qt-parsed-classes nil)

;; (DIR RECURSIVE CHECK OLD)
(defvar qt-class-list '("/usr/lib/qt/include" nil t qt-parsed-classes))

(defvar ez-class-list '("." nil t qt-parsed-classes))

(defvar project-known-classes nil)

(defun check-for-class( word )
  (interactive)
  (let ((classes project-classes)
	(known project-known-classes)
	know
	(ok nil)
	reg
	class)
    (while (and known (not ok))
      (setq know (car known))
      (if (string-match (car know) word)
	  (let ()
	    (if (car (cdr know))
		(message (concat "#include \"" (car (cdr (cdr know))) "\""))
	      (message (concat "#include <" (car (cdr (cdr know))) ">")))
	    (setq ok t )))
      (setq known (cdr known)))
    (if (not ok)
	(while classes
	  (setq class (car classes))
	  (setq reg (car class))
	  (if (string-match reg word)
	      (let ((expr (car (cdr class)))
		    retur
		    file
		    local
		    (cls nil))
		(setq retur (eval (append expr (list word))))
		(message "Found these classes:")
		(setq file (car retur))
		(setq local (car (cdr retur)))
		(setq retur (cdr (cdr retur)))
		(while retur
		  (message (car (car retur)))
		  (setq cls (cons (car (car retur)) cls))
		  (setq retur (cdr retur)))
		(if cls
		    (setq project-known-classes (cons (list (regexp-opt cls) local file) project-known-classes)))
		))
	  (setq classes (cdr classes))))))

(defun check-local-class(class word)
  (interactive)
  (let ((dir (car class))
	(recur (car (cdr class)))
	(check (car (cdr (cdr class))))
	(old (car (cdr (cdr class)))))
    (if (file-exists-p dir)
	(let (name
	      loname
	      hiname
	      (exts c++-header-extension-list)
	      ext
	      (ok nil)
	      include)
	  (while (and exts (not ok))
	    (setq ext (car exts))
	    (setq name (concat word "." ext))
	    (setq loname (concat (downcase word) "." ext))
	    (setq hiname (concat (upcase word) "." ext))
	    (cond
	     ((file-exists-p (concat dir "/" name))
	      (let ()
		(setq ok t)
		(setq include name)))
	     ((file-exists-p (concat dir "/" loname))
	      (let ()
		(setq ok t)
		(setq include loname)))
	     ((file-exists-p (concat dir "/" hiname))
	      (let ()
		(setq ok t)
		(setq include hiname))))
	    (setq exts (cdr exts)))
	  (if ok
	      (let ((buf (find-buffer-visiting (concat dir "/" include)))
		    classes)
		(message include)
		(if buf
		    (save-excursion
		      (set-buffer buf)
		      (beginning-of-buffer)
		      (setq classes (find-classes-in-buffer))
		      (setq classes (cons (or (string-equal dir "") (string-equal dir ".") (string-equal dir nil)) classes))
		      (setq classes (cons include classes))
		      classes)
		  (save-excursion
		    (setq buf (find-file (concat dir "/" include)))
		    (set-buffer buf)
		    (setq classes (find-classes-in-buffer))
		    (setq classes (cons (or (string-equal dir "") (string-equal dir ".") (string-equal dir nil)) classes))
		    (setq classes (cons include classes))
		    (kill-buffer buf)
;		    (message (car (car classes)))
		    classes
		    ))))
	  ))))

(defvar buffer-include-list nil)

(defvar c++-class-decl-regexp (concat 
			       "^"
			       ;; May have a template<>(1)
			       "\\(template[ \t\n]*<[^>]*>\\)?"
			       ;; class declaration and name(3)
			       "[ \t\n]*class\\([ \t\n]+\\([a-zA-Z_][a-zA-Z0-9_]*\\)\\)+"
			       ;; Ends in <>(4) if template
			       "\\(<[^>]*>\\)?"
			       ;; :
			       "\\([ \t\n]*:[ \t\n]*"
			       ;; public|protected|private(6)
			       "\\(public\\|protected\\|private\\)?"
			       ;; inherit name(7)
			       "[ \t\n]*\\([a-zA-Z_][a-zA-Z0-9_]*\\)"
			       ;; template(8)
			       "\\(<[^>]*>\\)?\\)?"
			       "[ \t]*\\(//[^\n]*\n\\)?[ \t\n]*"
			       ;; { or ; (10)
			       "[{]"
;;			       "\\(;\\|"
;;			       "{\\([^{]*{[^}]*}\\)*}[ \t\n]*;\\)"
			       ))

(defvar c++-protect-clause-regexp (concat
				   "^"
				   ;; public|protected|private(2) signals|slots(4)
				   "\\(\\(public\\|protected\\|private\\)"
				   "\\([ \t]*\\(signals\\|slots\\)\\)?"
				   "\\|"
				   ;; or signals|slots(5)
				   "\\(signals\\|slots\\)\\)"
				   ;; :
				   ":"))

(defvar c++-class-func-regexp (concat
			       "^[ \t]*"
			       "\\(template[ \t\n]*<[^>]*>\\)?"
			       ""
			       ))

(defun find-classes-in-buffer()
  (interactive)
  (let ((classes '())
	pos end
	(mpos (make-marker))
	(mend (make-marker)))
    (save-excursion
      (beginning-of-buffer)
      (while (search-forward-regexp
	      c++-class-decl-regexp
	      nil t)
	(setq pos (match-beginning 0))
	(setq end (match-end 0))
	(set-marker mpos pos)
	(set-marker mend end)
	(message (match-string-no-properties 10))
;	(if (string-equal (match-string-no-properties 10) "{")
	(setq classes (cons (list (match-string-no-properties 3) mpos mend) classes))
;	    )
	))
     classes))


(defun find-includes()
  (interactive)
  (let ((lst '()))
    (save-excursion
      (beginning-of-buffer)
      (while (search-forward-regexp (concat "^#include[ \t]+\\("
					    "\\(<[a-zA-Z0-9._\-]+>\\)\\|"
					    "\\(\"[a-zA-Z0-9._\-]+\"\\)"
					    "\\)")
				    nil t)
	(let ((res (match-string-no-properties 1))
	      (pos (match-beginning 0))
	      (end (match-end 0))
	      (mpos (make-marker))
	      (mend (make-marker)))
	  (set-marker mpos pos)
	  (set-marker mend end)
	  (setq lst (cons (list res mpos mend) lst)))))
    lst))

(defun include-exists( include )
  (interactive)
  (let ((incs buffer-include-list)
	inc
	(ok nil))
    (while (and incs (not ok))
      (setq inc (car incs))
      (if (string-equal (car inc) include)
	  (setq ok t))
      (setq incs (cdr incs)))
    ok))


; (global-set-key [(meta f11)] '(lambda()
; 			   (interactive)
; 			   (if (include-exists "<qstring.h>")
; 			       (message "yes")
; 			     (message "no"))))

(global-set-key [(meta f11)] '(lambda()
			   (interactive)
			   (let (buf)
			     (setq buf (generate-new-buffer "*classes*"))
			     (set-buffer buf)
			     (insert-string (concat ";; -*- Mode: Emacs-Lisp -*-\n"
						    ";; -*- lisp -*-\n"
						    ";; Project classes\n"
						    "\n"
						    ";; Automaticly insert these include files when found in c++ file\n"))
			     (insert-string (scan-directory-string "./" t "project-local-classes"))
			     (write-file "./.emacs-classes")
			     (kill-buffer buf))))

(global-set-key [(alt f11)] '(lambda()
			   (interactive)
			   (message (check-for-class "QListView"))))

(global-set-key [(alt f11)] '(lambda()
			   (interactive)
			   (message (find-classes-in-buffer))))

(global-set-key [f11] '(lambda ()
			 (interactive)
			 (make-variable-buffer-local 'buffer-include-list)
			 (setq buffer-include-list (find-includes))))


;; Checks for known classes and removes any unnecessary includes
(defun remove-include()
  "Removes #include on the top of the file if certain class names are not found in the file"
  (interactive)
  (if (string-equal mode-name "C++")
      (let ((includes project-include-classes)
	    (include)
	    (include-classes)
	    (include-class)
	    (include-file))
	(while includes
	  (setq include (car includes))
	  (setq include-classes (car include))
	  (setq include-file (car (cdr include)))
	  (save-excursion
	    (beginning-of-buffer)
	    (if (search-forward-regexp (concat "^#include[ \t]+"
					       include-file
					       "[ \t]*\n") nil t )
		(let ((start)
		      (end)
		      (class-exists nil))
		  (setq start (match-beginning 0))
		  (setq end (match-end 0))
		  (setq class-exists nil)
		  (while (and (not class-exists) include-classes)
		    (setq include-class (car include-classes))
		    (save-excursion
		      (beginning-of-buffer)
		      (while (search-forward-regexp (concat "\\<\\(" include-class "\\)\\>") nil t)
			(if (string-equal (match-string 1) include-class)
			    (let ()
			      (setq class-exists t)))))
		    (setq include-classes (cdr include-classes)))
		  (if (not class-exists)
		      (save-restriction
			(delete-region start end)))))
	    (setq includes (cdr includes)))))))

;; Make sure the include files are updated when saving
(add-hook 'write-file-hooks (lambda()
			      (interactive)
			      (if c++-auto-include-add
				  (insert-include))))
(add-hook 'write-file-hooks (lambda()
			      (interactive)
			      (if c++-auto-include-remove
				  (remove-include))))

;; Returns a buffer with the main project
;; Will try to load it from disk if not found in buffer list
(defun project-main ()
  "Finds the current project."
  (let (buffer)
    (let ((buffers (buffer-list))
	  file)
      (while buffers
	(setq file (buffer-file-name (car buffers)))
	(if file
	    (if (string-match project-regexp file)
		(setq buffer(car buffers))))
	(setq buffers (cdr buffers))))
    (if buffer
	()
      (let ((files (directory-files (expand-file-name ".") nil project-regexp t))
	    file
	    dir
	    (count 0))
	(setq dir files)
	(while files
	  (setq count (1+ count))
	  (setq files (cdr files)))
	(if (> count 0 )
	    (if (<= count 1)
		(if project-ask-load
		    (if (y-or-n-p (concat "Really load \"" (car dir) "\" from disk?"))
			(setq buffer (find-file (car dir))))
		  (setq buffer (find-file (car dir))))
	      (let ()
		(message "%d files found." count)
		(if (y-or-n-p (concat "Really load \"" (car dir) "\" from disk?"))
		    (setq buffer (find-file (car dir)))))))))
    buffer))

;; Returns a buffer with the main project
(defun project-main-in-buffers ()
  "Finds the current project."
  (let (buffer)
    (let ((buffers (buffer-list))
	  file)
      (while buffers
	(setq file (buffer-file-name (car buffers)))
	(if file
	    (if (string-match project-regexp file)
		(setq buffer(car buffers))))
	(setq buffers (cdr buffers))))
    buffer))


(defun project-file-list ( buffer tag )
  (if (stringp tag)
      (let ((lst nil))
	(save-excursion
	  (set-buffer buffer)
	  (beginning-of-buffer)
	  (if (search-forward-regexp (concat "^"
					     tag
					     "\\([ \t]*\=\\)[ \t]*\\(\\\\[ \t]*[\n]\\)?"
					     "\\(\\([ \t]*[a-zA-Z0-9\.\-/]+\\([ \t]*\\\\[ \t]*[\n]\\)?\\)*\\)")
				     nil t)
	      (save-restriction
		(beginning-of-buffer)
		(narrow-to-region (match-beginning 3) (match-end 3))
		(while (search-forward-regexp (concat "[ \t]*\\([a-zA-Z0-9\.\-/]+\\)"
						      "\\([ \t]*\\\\[ \t]*[\n]\\)?")
					      nil t)
		  (setq lst (cons (match-string-no-properties 1) lst))))))
	(nreverse lst))
    (error "Must supply a tag string" )))

(defun project-files ( project )
  (list (project-file-list project "SOURCES")
	(project-file-list project "HEADERS")))


(defun project-load-check ()
  (if (string-match project-regexp (buffer-name (current-buffer)))
      (project-update-menu)))

(add-hook 'find-file-hooks 'project-load-check)

(defun project-update-menu ()
  "Updates the project files menu with the files in the project"
  (interactive)
  (let* ((project (project-main))
	 (lst (project-files project))
	 (slst (car lst))
	 (hlst (car (cdr lst)))
	 bufdir)
    (setq bufdir (file-name-directory (buffer-file-name project)))
    (easy-menu-change '("files")
		      "Project Files"
		      (list (cons "Sources"
				  (mapcar '(lambda (entry)
					     (vector entry (list 'find-file (concat bufdir "/" entry)) t))
					  (car (project-files (project-main)))))
			    (cons "Headers"
				  (mapcar '(lambda (entry)
					     (vector entry (list 'find-file entry) t))
					  (car (cdr (project-files (project-main)))))))
		      "open-file")))

(defun project-projects ()
  "Returns a list of projects read from .projects

The syntax for the file is NAME=[FILE|DIR]"
  (let ((file (expand-file-name project-projects-file-name))
	projects)
    (if (file-exists-p file)
	(let ((buf (generate-new-buffer "Projects"))
	      name dir)
	  (setq project-projects-file-modification-time (nth 5 (file-attributes file)))
	  (save-excursion
	    (set-buffer buf)
	    (insert-file-contents-literally file)
	    (beginning-of-buffer)
	    (while (search-forward-regexp "^\\([a-zA-Z0-9_-]+\\)=\\(.+\\)$" nil t)
	      (setq name (match-string 1))
	      (setq dir (match-string 2))
	      (setq projects (nconc projects (list (cons name dir))))
	      (forward-line-nomark 1)))
	  (kill-buffer buf)))
    projects))

(defun project-load-project (name &optional file)
  "Loads a project called NAME using file/dir FILE
If called interactively the project is asked for in the minibuffer.
If file is not supplied it is searched for using NAME in the list returned by `project-projects'"
  (interactive (list (let ((prompt "Which project? ")
			   (completion-ignore-case t))
		       (completing-read prompt (project-projects) nil t
					(cons (or project-current-project "")
					      (length project-current-project))
					'project-select-project-history))))
  (if (null file)
      (setq file (project-file name)))
  (if (file-directory-p file)
      (progn
	(setq revive:configuration-file (concat file "/.revive.el"))
	(find-file file))
    (let ()
      (setq revive:configuration-file (concat (file-name-directory file) "/.revive.el"))
      (find-file file)
      (load-local-file-module "project")))
  (resume-try))

(defun project-file (name)
  "Returns the file/dir of the project named NAME from the project list.
Returns nil if the project is not found"
  (cdr (assoc name (project-projects))))

(defun project-update-project-menu ()
  "Updates the projects menu with the currently available projects"
  (interactive)
  (let ((projects (project-projects)))
    (easy-menu-change '("files")
		      "Projects"
		      (mapcar '(lambda (entry)
				 (vector (car entry) (list 'project-load-project (car entry) (cdr entry)) t))
			      projects)
		      "open-file")))

(defun project-file-changed ()
  "Updates the project menu if the file has changed on disk"
  (interactive)
  (if (or (null project-projects-file-modification-time)
	  (not (equal (nth 5 (file-attributes (expand-file-name project-projects-file-name)))
		      project-projects-file-modification-time)))
      (project-update-project-menu)))

(run-at-time nil 5 'project-file-changed)

;; Loads all header and source files found in the project file.
(defun project-load-files ()
  "Loads all the project files."
  (interactive)
  (let ((project (project-main))
	lst slst hlst)
    (if project
	(save-excursion
	  (setq lst (project-files project))
	  (setq slst (car lst))
	  (setq hlst (car (cdr lst)))
	  (while slst
	    (find-file-noselect (car slst))
	    (setq slst (cdr slst)))
	  (while hlst
	    (find-file-noselect (car hlst))
	    (setq hlst (cdr hlst))))
      (message "Couldn't find any projects \(In right directory ?\)."))))

(defun project-execute ()
  "Executes the exe file in the current project."
  (interactive)
  (let (name)
    (save-excursion
      (set-buffer (project-main))
      (beginning-of-buffer)
      (save-excursion
	(if (search-forward-regexp "^TARGET[ \t]*\=[ \t]*\\([a-zA-Z\.\-_]+[ \t]*\\)[\n]" nil t)
	    (save-restriction
	      (beginning-of-buffer)
	      (shell-command (concat "./" (match-string 1) " &") (get-buffer "*compilation*")))
	  (message "No target found"))))))

(defun project-debug ()
  "Debugs the exe file in the current project."
  (interactive)
  (let (name)
    (save-excursion
      (set-buffer (project-main))
      (beginning-of-buffer)
      (save-excursion
	(if (search-forward-regexp "^TARGET[ \t]*\=[ \t]*\\([a-zA-Z\.\-_]+[ \t]*\\)[\n]" nil t)
	    (save-restriction
	      (beginning-of-buffer)
	      (shell-command (concat project-debugger " " (match-string 1) " &") (get-buffer "*compilation*"))))))))

; Compiles the current program with options
(defun project-compile(opts)
  "Compile current project with options"
  (interactive "MEnter compile options: ")
  (save-excursion
    (let ((project (project-main)))
      (if project
	  (let ((object-dir (project-object-dir project)))
	    (set-buffer project)
	    (if (not (string-match object-dir ""))
		(if (not (file-exists-p object-dir))
		    (make-directory object-dir))))))
    (compile (concat "make " opts))))

;; Inserts a file into the project
(defun project-insert-file (project file keyword)
  "Insert a FILE into the current PROJECT buffer after the given KEYWORD."
  (let ((filename (file-relative-name file))
	data items)
    (setq data (project-get-line keyword nil nil project))
    (or (and data
	     (progn
	       (setq items (split-string (caddr data)))
	       (project-set-line (car data) (cadr data) (sort (append items (list filename)) 'string<) t project)))
	(error (concat "Keyword " keyword " not found in project")))))

;; Removes a file from the project
(defun project-remove-file (project file keyword)
  "Removes a FILE from the current PROJECT buffer after the give KEYWORD."
  (let ((filename (file-relative-name file))
	data items)
    (setq data (project-get-line keyword nil nil project))
    (or (and data
	     (progn
	       (setq items (split-string (caddr data)))
	       (project-set-line (car data) (cadr data) (sort (delete filename items) 'string<) t project)))
	(error (concat "Keyword " keyword " not found in project")))))

(defun project-replace-class-name (buffer classname oldname namespaces filename)
  "Replaces every occurence of project-normal-name-match,project-downcase-name-match and project-upcase-name-match with CLASSNAME,
in normal, downcase and upcase letters, in BUFFER."
  (let (full_up_name
	full_down_name
	full_name
	(nspaces namespaces))
    (while nspaces
      (setq full_up_name (concat full_up_name
				 (and full_up_name "_")
				 (upcase (car nspaces))))
      (setq full_down_name (concat full_down_name
				   (and full_down_name "_")
				   (downcase (car nspaces))))
      (setq full_name (concat full_name
			      (and full_name "::")
			      (car nspaces)))
      (setq nspaces (cdr nspaces)))
    (setq full_up_name (concat (and namespaces full_up_name)
			       (and namespaces "_")
			       (upcase classname)))
    (setq full_down_name (concat (and namespaces full_down_name)
				 (and namespaces "_")
				 (downcase classname)))
    (setq full_name (concat (and namespaces full_name)
			    (and namespaces "::")
			    classname))
    (save-excursion
      (set-buffer buffer)
      (beginning-of-buffer)
      (save-excursion
	(while (search-forward project-normal-fullname-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match full_name))))
      (save-excursion
	(while (search-forward project-downcase-fullname-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match full_down_name))))
      (save-excursion
	(while (search-forward project-upcase-fullname-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match full_up_name))))
      (save-excursion
	(while (search-forward project-normal-name-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match classname))))
      (save-excursion
	(while (search-forward project-downcase-name-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match (downcase classname)))))
      (save-excursion
	(while (search-forward project-upcase-name-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match (upcase classname)))))
      (save-excursion
	(while (search-forward project-deriveclass-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match oldname))))
      (save-excursion
	(while (search-forward project-filename-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match filename))))
      (save-excursion
	(while (search-forward project-namespace-start-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match "")
	    (let ((nspaces namespaces))
	      (while nspaces
		(insert-string (concat "namespace " (car nspaces)))
		(newline-and-indent)
		(insert-string "{")
		(newline-and-indent)
		(setq nspaces (cdr nspaces)))))))
      (save-excursion
	(while (search-forward project-namespace-end-match nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match "")
	    (let ((nspaces namespaces))
	      (while nspaces
		(insert-string "}")
		(newline-and-indent)
		(setq nspaces (cdr nspaces)))))))
      (save-excursion
	(while (search-forward "\<real-name\>" nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match user-full-name))))
      (save-excursion
	(while (search-forward "\<login-name\>" nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match user-login-name))))
      (save-excursion
	(while (search-forward "\<mail-name\>" nil t)
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (replace-match project-mail-account)))))))

;; Returns the project type, currently Qt or c++.
(defun project-type (project)
  "Returns type of project in use."
  (let (name)
    (save-excursion
      (set-buffer project)
      (beginning-of-buffer)
      (save-excursion
	(if (search-forward-regexp "^CONFIG[ \t]*\=[ \t]*\\(\\([a-zA-Z\.\-_]+[ \t]*\\)*\\)[\n]" nil t)
	    (save-restriction
	      (beginning-of-buffer)
	      (narrow-to-region (match-beginning 1) (match-end 1))
	      (if (search-forward "qt" nil t)
		  (setq name "Qt")
		(setq name "c++")))
	  (setq name "c++"))))
    name))


;; Returns a list of options
(defun project-config (project)
  "Returns project configuration."
  (interactive)
  (let (name)
    (save-excursion
      (set-buffer project)
      (beginning-of-buffer)
      (save-excursion
	(if (search-forward-regexp "^CONFIG[ \t]*\=[ \t]*\\(\\([a-zA-Z\.\-_]+[ \t]*\\)*\\)[\n]" nil t)
	    (save-restriction
	      (beginning-of-buffer)
	      (narrow-to-region (match-beginning 1) (match-end 1))
	      (while (search-forward-regexp "[a-zA-Z\.\-_]+" nil t)
		(let ()
		  (setq name (cons (match-string 0) name))))))
	(defvar project-config-opts name)))
    name))

;; Returns project object dir.
(defun project-object-dir (project)
  "Returns project object directory if exists."
  (interactive)
  (let (name)
    (save-excursion
      (set-buffer project)
      (beginning-of-buffer)
      (save-excursion
	(if (search-forward-regexp "^OBJECTS_DIR[ \t]*\=[ \t]*\\([a-zA-Z\.\-_]+[ \t]*\\)[\n]" nil t)
	    (setq name (match-string 1))
	  (setq name ""))))
    name))

;; Aks for project name and creates a new project
(defun project-new ()
  "Creates a new project if the no project exists"
  (interactive)
  (let ((project (project-main)))
    (if project
	(message "Project already exists.")
      (let (newproject
	    projectfile
	    projectbuf
	    mainbuf)
	(setq newproject (read-from-minibuffer (concat "Enter name of new project : ")))
	(setq projectfile (concat (downcase newproject)
				  ".pro"))
	(setq projectbuf (find-file projectfile))
	(project-replace-class-name projectbuf newproject "" nil (downcase newproject))
	(setq mainbuf (find-file-noselect "main.cpp"))
	(project-replace-class-name mainbuf "main" "" nil "main")
	(save-buffer)
;;	(desktop-save "./")
;; Use revive instead
	(if option-package-available-revive
	    (save-current-configuration))
	(shell-command (concat project-tmake-executable " -o Makefile " projectfile))
	(project-update-menu)))))

;; Returns the name of the project
(defun project-name (project)
  "Returns the name of the project."
  (let (name)
    (save-excursion
      (set-buffer project)
      (beginning-of-buffer)
      (if (search-forward-regexp "^PROJECT[ \t]*\=[ \t]*\\([a-zA-Z\.\-]+\\)[ \t]*[\n]" nil t)
	  (setq name (match-string 1))
	(setq name "Noname")))
    name))

(defun project-capitalize-ez-classname (name)
  "Capitalizes a classname NAME beginning with ez to the correct form which eZ and the rest capitalized"
  (if (< (length name) 2)
      name
    (concat "eZ" (capitalize (substring name 2)))))

(defvar project-capitalize-rules
  '(("^ez" . project-capitalize-ez-classname))
  "Captialize rules used with `project-capitalize-classname'")

(defun project-capitalize-classname (name)
  "Capitalizes the classname NAME according to the rules defined in `project-capitalize-rules'.
If no rule is found a normal `capitalize' is run on the classname."
  (let ((lst project-capitalize-rules)
	rule
	capitalize-func)
    (while lst
      (setq rule (car lst))
      (if (string-match (car rule) name)
	  (setq capitalize-func (cdr rule)))
      (setq lst (cdr lst)))
    (if capitalize-func
	(eval (list capitalize-func name))
      (capitalize name))))


;; Creates some customization files in the current directory
(defun project-create-custom-files ()
  "Add a class to the current project."
  (interactive)
  (let ((project (project-main))
	dir)
    (if project
	(setq dir (file-name-directory (buffer-file-name project)))
      (setq dir (expand-file-name ".")))
    (let* ((old-buffer (current-buffer))
	   (varfile ".emacs-vars")
	   (customfile ".emacs-custom")
	   (varbuffer (find-file-noselect varfile t t))
	   (custombuffer (find-file-noselect customfile t t))
	   auto-insert-files)
      (switch-to-buffer varbuffer)
      (if (> (buffer-size) 0)
	  (delete-region 1 (buffer-size)))
      (insert (concat ";; -*- Mode: Emacs-Lisp -*-\n;; -*- lisp -*-\n"
		      ";---------------------------------------------------------------------\n"
		      ";; This file was generated by emacs config Version "
		      (option-config-version) ", modify it to suit your needs\n\n"))
      (insert (concat ";; Set the default header extensions\n"
		      "(setq c++-default-header-ext \"" c++-default-header-ext "\")\n\n"))
      (insert (concat ";; Set the default source extensions\n"
		      "(setq c++-default-source-ext \"" c++-default-source-ext "\")\n\n"))
      (insert (concat ";; We use the local autoinsert files\n"
		      "(setq project-auto-insert-directory (concat (expand-file-name \".\") \"/.autoinsert/\"))\n"))
      (basic-save-buffer)
      (kill-buffer varbuffer)

      (switch-to-buffer custombuffer)
      (if (> (buffer-size) 0)
	  (delete-region 1 (buffer-size)))
      (insert (concat ";; -*- Mode: Emacs-Lisp -*-\n;; -*- lisp -*-\n"
		      ";---------------------------------------------------------------------\n"
		      ";; This file was generated by emacs config Version "
		      (option-config-version) ", modify it to suit your needs\n\n"))
      (insert (concat ";; Make sure the auto insert directory is properly updated\n"
		      "(setq auto-insert-directory (project-select-autoinsert))"))
      (basic-save-buffer)
      (kill-buffer custombuffer)

      (if (not (file-exists-p ".autoinsert"))
	  (make-directory ".autoinsert"))
      (setq auto-insert-files (directory-files "~/.autoinsert"))
      (mapcar '(lambda (x)
		 (if (not (or (string-equal "." x)
			       (string-equal ".." x)))
		     (copy-file (concat "~/.autoinsert/" x) (concat dir "/.autoinsert/" x))))
	      auto-insert-files)

      (switch-to-buffer old-buffer))))

(defun nth2 (num lst)
  (let ((val lst))
    (while (> num 0 )
      (setq num (- num))
      (setq val (cdr val)))
    val))

(defvar project-namespace-exclude-regexp nil
  "Regexp which matches all relative dirs which should not append the namespace to the filename")

(defun project-exclude-namespace (dir)
  (and project-namespace-exclude-regexp
       (string-match project-namespace-exclude-regexp dir)
       t))

;; Adds a class to the current project, creates the header and/or source file if non existing.
(defun class-add ()
  "Add a class to the current project."
  (interactive)
  (let ((project (project-main)))
    (if project
	(let (newclass
	      oldclass
	      oldname
	      headerfile
	      sourcefile
	      headerbuf
	      sourcebuf
	      proj_dir
	      real_headerfile
	      real_sourcefile
	      dest_dir dest_dir_name
	      namespace namespaces
	      basename
	      cur_dir)
	  (setq proj_dir (file-name-directory (buffer-file-name project)))
	  (setq cur_dir (file-name-directory (buffer-file-name (current-buffer))))
	  (setq newclass (read-from-minibuffer (concat "Enter class name to add to "
						       (project-type project)
						       " project \""
						       (project-name project)
						       "\": ")))
	  (setq oldname (read-from-minibuffer "Enter class to derive from(Enter for none): "))
	  (setq namespace (read-from-minibuffer "Enter namespace(s)(Enter for none): "))
	  (setq namespaces (split-string namespace "::"))
	  (setq dest_dir (file-relative-name  proj_dir))
	  (if (string-equal dest_dir "./")
	      (setq dest_dir ""))
	  (setq dest_dir (read-from-minibuffer "Enter relative destination directory (Enter for current): " dest_dir))
	  (if (string-equal dest_dir "")
	      ()
	    (setq dest_dir (file-name-as-directory dest_dir)))
	  (make-directory dest_dir t)
	  (if (eq oldname nil)
	      (setq oldname ""))
	  (if (not (string-equal oldname ""))
	      (setq oldclass (concat " : public " oldname))
	    (setq oldclass ""))
	  (setq dest_dir_name (file-relative-name (file-name-as-directory dest_dir) proj_dir))
	  (if project-namespace-filename-p
	      (mapcar '(lambda(x)
			 (setq basename (concat basename (downcase x))))
		      (if (project-exclude-namespace dest_dir_name)
			  nil
			(nth2 project-omit-namespaces namespaces))))
	  (setq headerfile (concat dest_dir basename (downcase newclass)
				"." c++-default-header-ext))
	  (setq sourcefile (concat dest_dir basename (downcase newclass)
				"." c++-default-source-ext))
	  (setq headerbuf (find-file-noselect headerfile))
	  (setq sourcebuf (find-file-noselect sourcefile))
	  (setq real_headerfile (file-relative-name (buffer-file-name headerbuf) proj_dir))
	  (setq real_sourcefile (file-relative-name (buffer-file-name sourcebuf) proj_dir))
	  (setq basename (concat basename (downcase newclass)))
	  (project-replace-class-name headerbuf newclass oldclass namespaces basename)
	  (save-excursion
	    (set-buffer headerbuf)
	    (beginning-of-buffer)
	    (end-of-buffer-mark)
	    (indent-region (point-min) (point-max) nil))
	  (if (not (string-equal oldname ""))
	      (setq oldclass (concat "\n    : " oldname "(===)" ))
	    (setq oldclass ""))
	  (project-replace-class-name sourcebuf newclass oldclass namespaces basename)
	  (save-excursion
	    (set-buffer sourcebuf)
	    (beginning-of-buffer)
	    (end-of-buffer-mark)
	    (indent-region (point-min) (point-max) nil))
	  (project-insert-params oldname headerbuf sourcebuf)
	  (project-insert-file project real_headerfile "HEADERS")
	  (project-insert-file project real_sourcefile "SOURCES")
	  (set-buffer project)
	  (shell-command (concat project-tmake-executable " -o Makefile "
				 (file-name-nondirectory (buffer-file-name project))))
	  (project-update-menu)
	  (switch-to-buffer sourcebuf)
	  (switch-to-buffer headerbuf))
      (message "Couldn't find any projects \(In right directory ?\).")
      )))

;; Removes a file from the current project, and deletes them if is set.
;; TODO: Request source directory for files as in class-add
(defun class-remove ()
  "Removes a class from the current project."
  (interactive)
  (let ((project (project-main)))
    (if project
	(let (newclass
	      headerfile
	      sourcefile
	      headerbuf
	      sourcebuf)
	  (setq newclass (read-from-minibuffer (concat "Enter class name to remove from "
						       (project-type project)
						       " project \""
						       (project-name project)
						       "\": ")))
	  (setq headerfile (concat (downcase newclass)
				   "." c++-default-header-ext))
	  (setq sourcefile (concat (downcase newclass)
				   "." c++-default-source-ext))
	  (let ((projectdir (file-name-directory (buffer-file-name project)))
		(headerbuffer (get-buffer headerfile))
		(sourcebuffer (get-buffer sourcefile))
		realheaderfile realsourcefile;;Absolute file names
		relheaderfile relsourcefile);;Relative files names
	    (setq realheaderfile (buffer-file-name headerbuffer))
	    (setq realsourcefile (buffer-file-name sourcebuffer))
	    (setq relheaderfile (file-relative-name realheaderfile projectdir))
	    (setq relsourcefile (file-relative-name realsourcefile projectdir))
	    (if project-delete-redundant
		(let ()
		  (if headerbuffer
		      (let ()
			(set-buffer headerbuffer)
			(save-buffer)
			(kill-this-buffer)))
		  (if (file-exists-p realheaderfile)
		      (if project-delete-confirm
			  (if (y-or-n-p (concat "Delete file " relheaderfile " "))
			      (delete-file realheaderfile))
			(delete-file realheaderfile)))

		  (if sourcebuffer
		      (let ()
			(set-buffer sourcebuffer)
			(save-buffer)
			(kill-this-buffer)))
		  (if (file-exists-p realsourcefile)
		      (if project-delete-confirm
			  (if (y-or-n-p (concat "Delete file " relsourcefile " "))
			      (delete-file realsourcefile))
			(delete-file realsourcefile)))
		  (if (file-exists-p (concat (file-name-directory realsourcefile) "moc_" sourcefile))
		      (delete-file (concat (file-name-directory realsourcefile) "moc_" sourcefile)))))
	    (project-remove-file project relheaderfile "HEADERS")
	    (project-remove-file project relsourcefile "SOURCES")
	    (set-buffer project)
	    (save-buffer)
	    (shell-command (concat project-tmake-executable " -o Makefile "
				   (buffer-file-name project)))
	    (project-update-menu)))
      (message "Couldn't find any projects \(In right directory ?\).")
      )))


(defconst project-c++-func-regexp (concat
			 "^"		; beginning of line is required
			 "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
			 "\\([a-zA-Z0-9_:]+[ \t]+\\)?" ; type specs; there can be no
			 "\\([a-zA-Z0-9_:]+[ \t]+\\)?" ; more than 3 tokens, right?

			 "\\("		; last type spec including */&
			 "[a-zA-Z0-9_:]+"
			 "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)" ; either pointer/ref sign or whitespace
			 "\\)?"		; if there is a last type spec
			 "\\("		; name; take that into the imenu entry
			 "[a-zA-Z0-9_:~]+" ; member function, ctor or dtor...
					; (may not contain * because then
				; "a::operator char*" would become "char*"!)
			 "\\|"
			 "\\([a-zA-Z0-9_:~]*::\\)?operator"
			 "[^a-zA-Z1-9_][^(]*" ; ...or operator
			 " \\)"
			 "[ \t]*([^)]*)[ \t\n]*[^ ;]" ; require something other than a ; after
			 ))


(defun c-project-menu (modestr)
  (let ((m
	 '(
	   ["New Project"      project-new (not (project-main-in-buffers))]
	   ["Load Project"     (set-buffer (project-main)) (not (project-main-in-buffers))]
	   ["Close Project"     (kill-buffer (project-main-in-buffers)) (project-main-in-buffers)]
	   ["Create Makefile"   (make-makefile) (project-main-in-buffers)]
	   ["Regenerate Makefile"   (project-regenerate-project-file) (project-main-in-buffers)]
	   ["Load All Sources"  project-load-files (project-main-in-buffers)]
	   "---"
	   ["Add Class"         class-add (project-main-in-buffers)]
	   ["Remove Class"      class-remove (project-main-in-buffers)]
	   "---"
	   ["Create Custom Files" project-create-custom-files t]
	   )))
    (cons modestr m)))

(defun project-test ()
  (interactive)
  (if (search-forward-regexp project-c++-func-regexp nil t )
      (message (match-string 0))))

(defun project-tag-file()
  (let ((tag "TAGS")
	(project (project-main))
	prodir
	tagfile)
    (setq prodir (file-name-directory (buffer-file-name project)))
    (setq tagfile (concat prodir tag))
    tagfile))

(defun project-load-tags()
;  (interactive)
  (let ((tagfile (project-tag-file)))
    (if (file-exists-p tagfile)
	(visit-tags-table tagfile))))

(defvar project-tag-list)

(defvar project-qt-tag-dir '("$QTDIR/src/dialogs/*.h" "$QTDIR/src/kernel/*.h"
			     "$QTDIR/src/tools/*.h" "$QTDIR/src/widgets/*.h"))

(defvar project-standard-tag-dir '("/usr/include/*.h" "/usr/include/bits/*.h"
				   "/usr/include/g++-2/*" "/usr/include/sys/*.h"))

(defun project-expand-tag-list (lst)
  (interactive)
  (let (dirs item items)
    (setq items lst)
    (while items
      (setq item (car items))
      (if dirs
	  (setq dirs (concat dirs " " item))
	(setq dirs item))
      (setq items (cdr items)))
    dirs))

(defun project-config-has ( var )
  (interactive)
  (let ((items (project-config (project-main)))
	item
	(found nil))
    (while items
      (setq item (car items))
      (if (string-equal item var)
	  (setq found t))
      (setq items (cdr items)))
    found))

(defun project-generate-tags()
  (interactive)
  (let (files
	(lst nil))
    (if (project-config-has "qt")
	(setq lst (nconc lst project-qt-tag-dir)))
    (setq lst (nconc lst project-standard-tag-dir))
    (setq files (project-expand-tag-list lst))
    (shell-command (concat "etags " files))
    ))
  

(defun project-expand-symbol( arg )
  (interactive "P")
  (if (not (file-exists-p (project-tag-file)))
      (project-generate-tags))
;       (project-load-tags)
  (complete-symbol arg))

(defun project-hide-entry ()
  (interactive)
  (if (outline-on-heading-p)
      (let ()
	(show-entry)
	(forward-char))
    (hide-entry)))

(defun project-regenerate-project-file ()
  "Makes sure the project file will be recreated.

For C++ projects using tmake this means that the Makefile will be recreated"
  (interactive)
  (let ((project (project-main)))
    (shell-command (concat "touch "
			   (buffer-file-name project)))))

; Scans a .hpp .h or .hh file when saved for the keyword Q_OBJECT
; if found then checks if the moc file exists for the the .cpp file
; if not runs tmake on the project file
;; (defun c++-moc-file ()
;;   "Runs tmake on the project if signals/slots has been added to the c++ header."
;;   (interactive)
;;   (if (string-match c++-header-ext-regexp (buffer-name))
;;       (save-excursion
;; 	(beginning-of-buffer)
;; 	(let ((filedir (file-relative-name (file-name-directory (buffer-file-name))))
;; 	      (filenonext (file-name-sans-extension (file-relative-name (file-name-directory (buffer-file-name))))))
;; 	  (if (search-forward-regexp "\\<Q_OBJECT\\>" nil t)
;; 	      (progn
;; 		(set-buffer (project-main))
;; 		(save-buffer)
;; 		(if (not (file-exists-p (concat filedir	"moc_" filenonext "." c++-default-source-ext)))
;; 		    (shell-command (concat project-tmake-executable " -o " filedir "Makefile " (file-name-nondirectory (buffer-file-name (project-main)))) )))
;; 	    (save-restriction
;; 	      (setq filename (concat filedir "moc_" filenonext "." c++-default-source-ext))
;; 	      (if (file-exists-p filename)
;; 		  (progn
;; 		    (set-buffer (project-main))
;; 		    (save-buffer)
;; 		    (shell-command (concat project-tmake-executable " -o " filedir "Makefile " (file-name-nondirectory (buffer-file-name (project-main)))))
;; 		    (delete-file filename))))))))
;;   nil)

; (add-hook 'after-save-hook 'c++-moc-file)

;; Inserts a Q_OBJECT in a c++ header file if slots of signals are used
(defun buffer-insert-qobject()
  (interactive)
  (save-excursion
    (beginning-of-buffer)
    (if (search-forward-regexp (regexp-opt '("slots" "signals") t) nil t)
	(let ()
	  (beginning-of-buffer)
	  (if (search-forward-regexp (concat "^\\(template[ \t]*<[^>]+>[ \t]*\\)?class[ \t]+\\([a-zA-Z0-9_]+\\)[ \t\n]*"
					     "\\([:][ \t\n]*\\(public\\|protected\\|private\\)?[ \t\n]*\\<[a-zA-Z0-9_]+\\>\\)?"
					     "[ \t\n]*{")
				     nil t)
	      (if (not (looking-at "[ \t\n]*\\(Q_OBJECT\\)"))
		  (insert-string "\n\tQ_OBJECT"))
	    (ding))))))

;(add-hook 'write-file-hooks '(lambda ()
;			       (interactive)
;			       (if (string-match c++-header-ext-regexp (buffer-name))
;				   (buffer-insert-qobject))))

;; Adds index to the menu for lisp and c/c++ modes.
(defun project-add-index ()
  (if (and (emacs-type-is-regular)
	   (or (string-match mode-name "Emacs-Lisp")
	       (string-match mode-name "C++")
	       (string-match mode-name "C")))
      (imenu-add-menubar-index)))

;; Creates the index after the file has been loaded.
(add-hook 'find-file-hooks 'project-add-index)

