PageRenderTime 26ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/GMList.cpp

https://code.google.com/p/gogglesmm/
C++ | 401 lines | 277 code | 70 blank | 54 comment | 71 complexity | 90836f5227b25a18f6b6a725266feb14 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*******************************************************************************
  2. * Goggles Music Manager *
  3. ********************************************************************************
  4. * Copyright (C) 2006-2010 by Sander Jansen. All Rights Reserved *
  5. * --- *
  6. * This program is free software: you can redistribute it and/or modify *
  7. * it under the terms of the GNU General Public License as published by *
  8. * the Free Software Foundation, either version 3 of the License, or *
  9. * (at your option) any later version. *
  10. * *
  11. * This program is distributed in the hope that it will be useful, *
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  14. * GNU General Public License for more details. *
  15. * *
  16. * You should have received a copy of the GNU General Public License *
  17. * along with this program. If not, see http://www.gnu.org/licenses. *
  18. ********************************************************************************/
  19. #include "gmdefs.h"
  20. #include "GMTrack.h"
  21. #include "GMApp.h"
  22. #include "GMTrackList.h"
  23. #include "GMSource.h"
  24. #include "GMPlayerManager.h"
  25. #include "GMTrackView.h"
  26. #include "GMCoverCache.h"
  27. #include "GMList.h"
  28. #define ICON_SPACING 4 // Spacing between icon and label
  29. #define SIDE_SPACING 6 // Left or right spacing between items
  30. #define LINE_SPACING 4 // Line spacing between items
  31. static inline FXbool begins_with_keyword(const FXString & t){
  32. const FXStringList & keywords = GMPlayerManager::instance()->getPreferences().gui_sort_keywords;
  33. for (FXint i=0;i<keywords.no();i++){
  34. if (comparecase(t,keywords[i],keywords[i].length())==0) return true;
  35. }
  36. return false;
  37. }
  38. FXint genre_list_sort(const FXListItem* pa,const FXListItem* pb){
  39. return comparecase(pa->getText(),pb->getText());
  40. }
  41. FXint genre_list_sort_reverse(const FXListItem* pa,const FXListItem* pb){
  42. return -comparecase(pa->getText(),pb->getText());
  43. }
  44. FXint generic_name_sort(const FXListItem* pa,const FXListItem* pb){
  45. register FXint a=0,b=0;
  46. if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1);
  47. if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1);
  48. return comparecase(&pa->getText()[a],&pb->getText()[b]);
  49. }
  50. FXint generic_name_sort_reverse(const FXListItem* pa,const FXListItem* pb){
  51. register FXint a=0,b=0;
  52. if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1);
  53. if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1);
  54. return comparecase(&pb->getText()[b],&pa->getText()[a]);
  55. }
  56. FXint source_list_sort(const FXTreeItem* pa,const FXTreeItem* pb){
  57. const GMSource * const sa = (const GMSource*)pa->getData();
  58. const GMSource * const sb = (const GMSource*)pb->getData();
  59. if (sa->getType()>sb->getType()) return 1;
  60. else if (sa->getType()<sb->getType()) return -1;
  61. register FXint a=0,b=0;
  62. if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1);
  63. if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1);
  64. return compareversion(&pa->getText()[a],&pb->getText()[b]);
  65. }
  66. FXint source_list_sort_reverse(const FXTreeItem* pa,const FXTreeItem* pb){
  67. const GMSource * const sa = (const GMSource*)pa->getData();
  68. const GMSource * const sb = (const GMSource*)pb->getData();
  69. if (sa->getType()>sb->getType()) return 1;
  70. else if (sa->getType()<sb->getType()) return -1;
  71. register FXint a=0,b=0;
  72. if (begins_with_keyword(pa->getText())) a=FXMIN(pa->getText().length()-1,pa->getText().find(' ')+1);
  73. if (begins_with_keyword(pb->getText())) b=FXMIN(pb->getText().length()-1,pb->getText().find(' ')+1);
  74. return -compareversion(&pa->getText()[a],&pb->getText()[b]);
  75. }
  76. // Object implementation
  77. FXIMPLEMENT(GMListItem,FXListItem,NULL,0)
  78. // Draw item
  79. void GMListItem::draw(const GMList* list,FXDC& dc,FXint xx,FXint yy,FXint ww,FXint hh) const {
  80. register FXFont *font=list->getFont();
  81. register FXint ih=0,th=0;
  82. if(icon) ih=icon->getHeight();
  83. if(!label.empty()) th=font->getFontHeight();
  84. if(isSelected())
  85. dc.setForeground(list->getSelBackColor());
  86. // else
  87. // dc.setForeground(list->getBackColor()); // FIXME maybe paint background in onPaint?
  88. dc.fillRectangle(xx,yy,ww,hh);
  89. if(hasFocus()){
  90. dc.drawFocusRectangle(xx+1,yy+1,ww-2,hh-2);
  91. }
  92. xx+=SIDE_SPACING/2;
  93. if(icon){
  94. dc.drawIcon(icon,xx,yy+(hh-ih)/2);
  95. xx+=ICON_SPACING+icon->getWidth();
  96. }
  97. if(!label.empty()){
  98. if(!isEnabled())
  99. dc.setForeground(makeShadowColor(list->getBackColor()));
  100. else if(isSelected())
  101. dc.setForeground(list->getSelTextColor());
  102. else
  103. dc.setForeground(list->getTextColor());
  104. dc.drawText(xx,yy+(hh-th)/2+font->getFontAscent(),label);
  105. }
  106. }
  107. // Map
  108. FXDEFMAP(GMList) GMListMap[]={
  109. FXMAPFUNC(SEL_PAINT,0,GMList::onPaint),
  110. };
  111. // Object implementation
  112. FXIMPLEMENT(GMList,FXList,GMListMap,ARRAYNUMBER(GMListMap))
  113. // List
  114. GMList::GMList(){
  115. rowcolor=GMPlayerManager::instance()->getPreferences().gui_row_color;
  116. }
  117. GMList::GMList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h) : FXList(p,tgt,sel,opts,x,y,w,h) {
  118. rowcolor=GMPlayerManager::instance()->getPreferences().gui_row_color;
  119. thickfont=font;
  120. delete vertical;
  121. delete horizontal;
  122. delete corner;
  123. vertical=new GMScrollBar(this,this,GMList::ID_VSCROLLED,SCROLLBAR_VERTICAL);
  124. horizontal=new GMScrollBar(this,this,GMList::ID_HSCROLLED,SCROLLBAR_HORIZONTAL);
  125. corner=new GMScrollCorner(this);
  126. }
  127. GMList::~GMList(){
  128. }
  129. // Create custom item
  130. FXListItem *GMList::createItem(const FXString& text,FXIcon* icon,void* ptr){
  131. return new GMListItem(text,icon,ptr);
  132. }
  133. // Draw item list
  134. long GMList::onPaint(FXObject*,FXSelector,void* ptr){
  135. FXEvent* event=(FXEvent*)ptr;
  136. FXDCWindow dc(this,event);
  137. FXint i,y,h;
  138. // Set font
  139. dc.setFont(font);
  140. // Paint items
  141. y=pos_y;
  142. for(i=0; i<items.no(); i++){
  143. h=items[i]->getHeight(this);
  144. if(event->rect.y<=y+h && y<event->rect.y+event->rect.h){
  145. if (i%2)
  146. dc.setForeground(rowcolor);
  147. else
  148. dc.setForeground(backColor);
  149. if (items[i]->getData()==(void*)(FXival)-1)
  150. dc.setFont(thickfont);
  151. else
  152. dc.setFont(font);
  153. ((GMListItem*)items[i])->draw(this,dc,pos_x,y,FXMAX(listWidth,getVisibleWidth()),h);
  154. }
  155. y+=h;
  156. }
  157. // Paint blank area below items
  158. if(y<event->rect.y+event->rect.h){
  159. dc.setForeground(backColor);
  160. dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y);
  161. }
  162. return 1;
  163. }
  164. // Object implementation
  165. FXIMPLEMENT(GMTreeItem,FXTreeItem,NULL,0)
  166. GMTreeItem::GMTreeItem(){
  167. }
  168. // Get item height
  169. FXint GMTreeItem::getHeight(const FXTreeList* list) const {
  170. register FXFont *font=list->getFont();
  171. register FXint th=0,oih=0,cih=0;
  172. if(openIcon) oih=openIcon->getHeight();
  173. if(closedIcon) cih=closedIcon->getHeight();
  174. if(!label.empty()) th=font->getFontHeight();
  175. // if (oih>16) oih+=4;
  176. // if (cih>16) cih+=4;
  177. return FXMAX3(th,oih,cih)+4;
  178. }
  179. // Draw item
  180. void GMTreeItem::draw(const FXTreeList* list,FXDC& dc,FXint xx,FXint yy,FXint /* ww*/,FXint hh) const {
  181. register FXIcon *icon=(state&OPENED)?openIcon:closedIcon;
  182. register FXFont *font=list->getFont();
  183. register FXint th=0,tw=0,ih=0,iw=0;//ox=xx,oy=yy;
  184. xx+=SIDE_SPACING/2;
  185. if(icon){
  186. iw=icon->getWidth();
  187. ih=icon->getHeight();
  188. dc.drawIcon(icon,xx,yy+(hh-ih)/2);
  189. xx+=ICON_SPACING+iw;
  190. }
  191. if(!label.empty()){
  192. tw=4+font->getTextWidth(label.text(),label.length());
  193. th=4+font->getFontHeight();
  194. yy+=(hh-th)/2;
  195. // if(isSelected()){
  196. // dc.setForeground(list->getSelBackColor());
  197. // dc.fillRectangle(xx,yy,tw,th);
  198. // dc.fillRectangle(ox,oy,ww,hh);
  199. // }
  200. if(hasFocus()){
  201. dc.drawFocusRectangle(xx+1,yy+1,tw-2,th-2);
  202. }
  203. if(!isEnabled())
  204. dc.setForeground(makeShadowColor(list->getBackColor()));
  205. else if(isSelected())
  206. dc.setForeground(list->getSelTextColor());
  207. else
  208. dc.setForeground(list->getTextColor());
  209. dc.drawText(xx+2,yy+font->getFontAscent()+2,label);
  210. }
  211. }
  212. #define DEFAULT_INDENT 8 // Indent between parent and child
  213. #define HALFBOX_SIZE 4 // Half box size
  214. #define BOX_FUDGE 3 // Fudge border around box
  215. // Map
  216. FXDEFMAP(GMTreeList) GMTreeListMap[]={
  217. FXMAPFUNC(SEL_PAINT,0,GMTreeList::onPaint)
  218. };
  219. // Object implementation
  220. FXIMPLEMENT(GMTreeList,FXTreeList,GMTreeListMap,ARRAYNUMBER(GMTreeListMap))
  221. GMTreeList::GMTreeList(){
  222. }
  223. /// Construct a new, initially empty tree list
  224. GMTreeList::GMTreeList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h) : FXTreeList(p,tgt,sel,opts,x,y,w,h) {
  225. rowcolor=GMPlayerManager::instance()->getPreferences().gui_row_color;
  226. GMScrollArea::replaceScrollbars(this);
  227. }
  228. FXTreeItem* GMTreeList::createItem(const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr) {
  229. return (FXTreeItem*)new GMTreeItem(text,oi,ci,ptr);
  230. }
  231. // Draw item list
  232. long GMTreeList::onPaint(FXObject*,FXSelector,void* ptr){
  233. FXEvent* event=(FXEvent*)ptr;
  234. FXTreeItem* item=firstitem;
  235. FXTreeItem* p;
  236. FXint yh,xh,x,y,w,h,xp,hh,cc=0;
  237. FXDCWindow dc(this,event);
  238. dc.setFont(font);
  239. x=pos_x;
  240. y=pos_y;
  241. if(options&TREELIST_ROOT_BOXES) x+=(4+indent);
  242. while(item && y<event->rect.y+event->rect.h){
  243. w=item->getWidth(this);
  244. h=item->getHeight(this);
  245. cc++;
  246. if(event->rect.y<=y+h){
  247. // Draw item
  248. dc.setForeground(backColor);
  249. dc.fillRectangle(0,y,width,h);
  250. if (!item->isSelected()) {
  251. if (cc%2) {
  252. // dc.setForeground(backColor);
  253. // dc.fillRectangle(0,y,x+2,h);
  254. dc.setForeground(rowcolor);
  255. dc.fillRectangle(x,y,width-x,h);
  256. // dc.fillRectangle(x+2,y,width-x-2,h);
  257. }
  258. else {
  259. dc.setForeground(backColor);
  260. dc.fillRectangle(0,y,width,h);
  261. }
  262. }
  263. else {
  264. // dc.setForeground(backColor);
  265. // dc.fillRectangle(0,y,x+2,h);
  266. dc.setForeground(getSelBackColor());
  267. // dc.fillRectangle(x+2,y,width-x-2,h);
  268. dc.fillRectangle(x,y,width-x,h);
  269. }
  270. ((GMTreeItem*)item)->draw(this,dc,x,y,w,h);
  271. // Show other paraphernalia such as dotted lines and expand-boxes
  272. if((options&(TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES)) && (((GMTreeItem*)item)->parent || (options&TREELIST_ROOT_BOXES))){
  273. hh=h/2;
  274. yh=y+hh;
  275. xh=x-indent+(SIDE_SPACING/2);
  276. dc.setForeground(lineColor);
  277. dc.setBackground(backColor);
  278. dc.setStipple(STIPPLE_GRAY,pos_x&1,pos_y&1);
  279. if(options&TREELIST_SHOWS_LINES){ // Connect items with lines
  280. p=((GMTreeItem*)item)->parent;
  281. xp=xh;
  282. dc.setFillStyle(FILL_OPAQUESTIPPLED);
  283. while(p){
  284. xp-=(indent+p->getHeight(this)/2);
  285. if(((GMTreeItem*)p)->next) dc.fillRectangle(xp,y,1,h);
  286. p=((GMTreeItem*)p)->parent;
  287. }
  288. if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){
  289. if(((GMTreeItem*)item)->prev || ((GMTreeItem*)item)->parent) dc.fillRectangle(xh,y,1,yh-y-HALFBOX_SIZE);
  290. if(((GMTreeItem*)item)->next) dc.fillRectangle(xh,yh+HALFBOX_SIZE,1,y+h-yh-HALFBOX_SIZE);
  291. }
  292. else{
  293. if(((GMTreeItem*)item)->prev || ((GMTreeItem*)item)->parent) dc.fillRectangle(xh,y,1,hh);
  294. if(((GMTreeItem*)item)->next) dc.fillRectangle(xh,yh,1,h);
  295. dc.fillRectangle(xh,yh,x+(SIDE_SPACING/2)-2-xh,1);
  296. }
  297. dc.setFillStyle(FILL_SOLID);
  298. }
  299. // Boxes before items for expand/collapse of item
  300. if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){
  301. dc.setFillStyle(FILL_OPAQUESTIPPLED);
  302. dc.fillRectangle(xh+4,yh,(SIDE_SPACING/2)-2,1);
  303. dc.setFillStyle(FILL_SOLID);
  304. dc.drawRectangle(xh-HALFBOX_SIZE,yh-HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE);
  305. dc.setForeground(textColor);
  306. dc.fillRectangle(xh-HALFBOX_SIZE+2,yh,HALFBOX_SIZE+HALFBOX_SIZE-3,1);
  307. if(!(options&TREELIST_AUTOSELECT) && !item->isExpanded()){
  308. dc.fillRectangle(xh,yh-HALFBOX_SIZE+2,1,HALFBOX_SIZE+HALFBOX_SIZE-3);
  309. }
  310. }
  311. }
  312. }
  313. y+=h;
  314. // Move on to the next item
  315. if(((GMTreeItem*)item)->first && ((options&TREELIST_AUTOSELECT) || ((GMTreeItem*)item)->isExpanded())){
  316. x+=(indent+h/2);
  317. item=((GMTreeItem*)item)->first;
  318. continue;
  319. }
  320. while(!((GMTreeItem*)item)->next && ((GMTreeItem*)item)->parent){
  321. item=((GMTreeItem*)item)->parent;
  322. x-=(indent+item->getHeight(this)/2);
  323. }
  324. item=((GMTreeItem*)item)->next;
  325. }
  326. if(y<event->rect.y+event->rect.h){
  327. dc.setForeground(backColor);
  328. dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y);
  329. }
  330. return 1;
  331. }