{ ******************************************************************************** YaPeTaVi - Yet another Periodic Table Viewer Copyright (C) 2009-2012 Geoffray Levasseur . All rights reserved. http://www.geoffray-levasseur.org/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************** Description: Icons "on the fly" manager } unit uIconManager; {$mode objfpc}{$H+} interface uses Controls, Classes, SysUtils, Forms, Graphics; type TIconSize = (is8x8, is16x16, is22x22, is32x32, is48x48); TIconSetsList = array[0..127] of string; const IconSizeDir: array[TIconSize] of string = ('8x8', '16x16', '22x22', '32x32', '48x48'); DefaultTheme = 'oxygen'; //this not the iconset name but its directory name DefaultIconSize: TIconSize = is16x16; DefaultButtonIcon = 'actions/cancel.png'; NumberOfIcons = 39; {$ifndef WINDOWS} NewIconNames: array[0..NumberOfIcons - 1] of string = ('actions/document-new.png', 'actions/document-new-from-template.png', //0 'actions/document-open.png', 'actions/view-refresh.png', //2 'actions/document-save.png', 'actions/document-save-all.png', //4 'actions/dialog-close.png', 'actions/document-print.png', //6 'actions/document-preview.png', 'actions/application-exit.png', //8 'actions/document-print-preview.png', 'actions/edit-undo.png', //10 'actions/edit-redo.png', 'actions/edit-cut.png', //12 'actions/edit-copy.png', 'actions/edit-paste.png', //14 'apps/preferences-system-time.png', 'actions/insert-text.png', //16 'actions/mail-send.png', 'actions/document-save-as.png', //18 'actions/document-export.png', 'actions/document-open-recent.png', //20 'actions/dialog-ok.png', 'actions/dialog-cancel.png', //22 'actions/dialog-ok-apply.png', 'actions/dialog-close.png', //24 'actions/help-about.png', 'actions/edit-find.png', //26 'actions/go-next.png', 'actions/go-previous.png', //28 'actions/go-jump.png', 'actions/help-contents.png', //30 'actions/edit-clear.png', 'actions/system-run.png', //32 'actions/configure.png', 'actions/help-hint.png', //34 'actions/list-add.png', 'actions/list-remove.png', //36 'actions/im-status-message-edit.png'); {$else} NewIconNames: array[0..NumberOfIcons - 1] of string = ('document-new.png', 'document-new-from-template.png', //0 'document-open.png', 'view-refresh.png', //2 'document-save.png', 'document-save-all.png', //4 'dialog-close.png', 'document-print.png', //6 'document-preview.png', 'application-exit.png', //8 'document-print-preview.png', 'edit-undo.png', //10 'edit-redo.png', 'edit-cut.png', //12 'edit-copy.png', 'edit-paste.png', //14 'preferences-system-time.png', 'insert-text.png', //16 'mail-send.png', 'document-save-as.png', //18 'document-export.png', 'document-open-recent.png', //20 'dialog-ok.png', 'dialog-cancel.png', //22 'dialog-ok-apply.png', 'dialog-close.png', //24 'help-about.png', 'edit-find.png', //26 'go-next.png', 'go-previous.png', //28 'go-jump.png', 'help-contents.png', //30 'edit-clear.png', 'system-run.png', //32 'configure.png', 'help-hint.png', //34 'list-add.png', 'list-remove.png', //36 'im-status-message-edit.png'); {$endif} //this is more easy than learning icon's index const icClose = 6; icCancel = 23; icYes = 22; icNo = 6; icOk = 22; icApply = 24; icNew = 0; icOpen = 2; icSave = 4; icSaveAll = 5; icExit = 9; icUndo = 11; icRedo = 12; icCopy = 14; icCut = 13; icPaste = 15; icRefresh = 3; icAbout = 26; icAdd = 36; icChange = 38; icRemove = 37; icDelete = icRemove; icUp = 40; icDown = 39; icSystem = 33; var ilDefault: TImageList; IconSetsList: TIconSetsList; //load a new theme and apply the changes procedure LoadIconSet(Path, Theme: string; Size: TIconSize); //set button icons on the fly procedure InitBtnGlyphs(Form: TForm); //find Oxygen icon set (default) function FindOxygen: string; //get a list of installed icon set procedure GetIconSets(BaseDir: string; var IcSets: TIconSetsList); //get the home of icon sets deppending on the host OS function GetIconSetsBaseDir: string; //for color configuration create an icon on the fly showing the selected color function CreateColorGlyph(Color: TColor; Width, Height: integer): TBitmap; implementation uses Dialogs, Buttons, uConst, uUtils, uDebug; function CreateColorGlyph(Color: TColor; Width, Height: integer): TBitmap; var X, Y: integer; begin //create an empty bitmap and set its size Result := TBitmap.Create; Result.SetSize(Width, Height); //fill it with the given color for X := 0 to Width do for Y := 0 to Height do Result.Canvas.Pixels[X, Y] := Color; end; function AddIcon(Index: Integer; Path: string): Boolean; var Bitmap: TPortableNetworkGraphic; X, Y: integer; begin PrintDbg('Add icon ' + Path + NewIconNames[Index] + '... ', vlHigh); //only png format is supported Bitmap := TPortableNetworkGraphic.Create; if not FileExists(Path + NewIconNames[Index]) then //the icon is not present begin //we have to create an empty bitmap (and hopping it's gonna be seen as all transparent) PrintDbgResult(' not fount, create empty one: ', vlHigh); Bitmap.SetSize(ilDefault.Width, ilDefault.Height); for X := 0 to ilDefault.Width do for Y := 0 to ilDefault.Height do //actually this draw a boxed X if (X = Y) or (ilDefault.Width - X = Y + 1) or (X = 0) or (Y = 0) or (X = ilDefault.Width - 1) or (Y = ilDefault.Height - 1) then Bitmap.Canvas.Pixels[X, Y] := clBtnText else Bitmap.Canvas.Pixels[X, Y] := clBtnFace; //set transparancy settings Bitmap.TransparentColor := clBtnFace; Bitmap.Transparent := True; end else begin //load the bitmap and set transparency on Bitmap.LoadFromFile(Path + NewIconNames[Index]); Bitmap.Transparent := True; end; //add this to the image list ilDefault.Add(Bitmap, nil); PrintDbgResult(True, vlHigh); end; function FindOxygen: string; {$warning add BSD, MacOs and Unix support if different path} {$warning we should give user the ability to give that path manually} begin {$ifdef WINDOWS} //Oxygen is not supposed to exist with Windows so its bringed with the app Result := sPrgmPath + '..\icons\Oxygen\'; {$else} //if KDE (or just Oxygen) is installed on unixes, try to find it if DirectoryExists('/usr/share/icons/oxygen') then //debian, *ubuntu, opensuse Result := '/usr/share/icons/oxygen/' else if DirectoryExists('/opt/kde/share/icons/oxygen') then //fedora Result := '/opt/kde/share/icons/oxygen/' else if DirectoryExists('/opt/kde4/share/icons/oxygen') then //mandriva Result := '/opt/kde4/share/icons/oxygen/' else Result := ''; //probably not installed or non standard paths {$endif} PrintLnDbg('Found Oxygen icons: ' + Result, vlLow); end; procedure LoadIconSet(Path, Theme: string; Size: TIconSize); var IconBaseDir: string; I: Integer; begin //Size := is16x16; //set iconset path correctly in a unix minded approach IconBaseDir := Path; if IconBaseDir[Length(IconBaseDir)] <> DirectorySeparator then IconBaseDir := IconBaseDir + DirectorySeparator; if Theme[Length(Theme)] <> DirectorySeparator then IconBaseDir := IconBaseDir + Theme + DirectorySeparator else IconBaseDir := IconBaseDir + Theme; IconBaseDir := IconBaseDir + IconSizeDir[Size] + DirectorySeparator; PrintDbg('-- Trying to find icon theme ' + IconBaseDir, vlHigh); Inc(DebugLevel); //check iconset existance if not DirectoryExists(IconBaseDir) then begin //if unavailable rolling back to Oxygen PrintLnDbg('', vlHigh); PrintLnDbg('*** uIconManager.LoadIconSet: ERROR: ' + IconBaseDir + ' directory do not exists! Trying to find Oxygen...', vlHigh); IconBaseDir := FindOxygen; if IconBaseDir <> '' then begin PrintLnDbg('Found Oxygen icons: ' + IconBaseDir, vlHigh); IconBaseDir := IconBaseDir + IconSizeDir[Size] + DirectorySeparator; PrintDbg('Loading Oxygen in ' + IconBaseDir, vlHigh); end else begin //no Oxygen as well: show a warning PrintLnDbg('*** uIconManager.LoadIconSet: ERROR: Can''t find Oxygen ' + 'icons: No icon available, please choose a valid icon theme!', vlHigh); PrintLnDbg('!!! uIconManager.LoadIconSet: Warning: The application ' + 'may look uggly but should be usable.', vlHigh); Dec(DebugLevel); PrintLnDbg('-- *** Errors remainings', vlHigh); Exit; //find oxigen here end; end else write('.'); try //free the image list to be sure it'll not poluted with previous datas ilDefault.Free; write('.'); finally //create an image liste with the good size case DefaultIconSize of is8x8: ilDefault := TImageList.CreateSize(8, 8); is16x16: ilDefault := TImageList.CreateSize(16, 16); is22x22: ilDefault := TImageList.CreateSize(22, 22); is32x32: ilDefault := TImageList.CreateSize(32, 32); end; write('.'); end; Dec(DebugLevel); PrintLnDbg('-- Done', vlHigh); //add the icons in the newly created image list for I:= 0 to NumberOfIcons - 1 do AddIcon(I, IconBaseDir); end; procedure InitBtnGlyphs(Form: TForm); var I: integer; Bitmap: TBitmap; begin for I := 0 to Form.ComponentCount - 1 do if (Form.Components[I] is TBitBtn) and (Form.Components[I].Tag <> -1) then try PrintDbg('Init ' + Form.Components[I].Name + ' button glyph... ', vlHigh); Bitmap := TBitmap.Create; ilDefault.GetBitmap((Form.Components[I] as TBitBtn).Tag, Bitmap); (Form.Components[I] as TBitBtn).Glyph := Bitmap; PrintDbgResult(True, vlHigh); except PrintDbgResult(False, vlHigh); end; end; procedure GetIconSets(BaseDir: string; var IcSets: TIconSetsList); var SR: TSearchRec; I: integer; begin for I := 0 to 127 do IcSets[I] := ''; I := 0; if FindFirst(BaseDir + '*', faDirectory, SR) = 0 then //get subdirs repeat PrintDbg('Found ' + BaseDir + SR.Name + ' directory, checking for icons...', vlLow); //if there's 16x16 dir this should really be icons if DirectoryExists(BaseDir + SR.Name + '/16x16') then if I <= 127 then begin IcSets[I] := SR.Name; PrintDbgResult(' yes', vlLow); Inc(I); end else PrintDbgResult(' error: too many icon sets', vlLow) else PrintDbgResult(' no', vlLow); until FindNext(SR) <> 0; FindClose(SR); end; function GetIconSetsBaseDir: string; begin {$ifdef WINDOWS} Result := sPrgmPath + '..\icons\'; {$else} if DirectoryExists('/usr/share/icons/') then Result := '/usr/share/icons/' else if DirectoryExists('/opt/kde/share/icons/') then Result := '/opt/kde/share/icons/oxygen/' else if DirectoryExists('/opt/kde4/share/icons/') then Result := '/opt/kde4/share/icons/oxygen/' else if DirectoryExists('/opt/kde/share/icons/') then Result := '/opt/gnome/share/icons/' else Result := ''; //probably not installed or non standard paths {$endif} PrintLnDbg('Found base icon sets dir: ' + Result, vlLow); end; initialization end.