If load-icons-later is 2, then will skip auto loading icons entirely
[pandora-libraries.git] / minimenu / mmui.c
index 0c9a5f1..bb8a4ef 100644 (file)
 #define CHANGED_EVERYTHING  (1<<4)  /* redraw it all! */
 unsigned int render_mask = CHANGED_EVERYTHING;
 
+SDL_Thread *g_icon_thread = NULL;
+unsigned char g_icon_thread_stop = 0; /* if !0 means thread should stop and maim app may block until it goes back to 0.. */
+unsigned char g_icon_thread_busy = 0; /* if !0 means thread is running right now */
+
 #define MIMETYPE_EXE "/usr/bin/file"     /* check for file type prior to invocation */
 
 /* SDL
@@ -91,6 +95,8 @@ unsigned char ui_detail_hidden = 0;     // if >0, detail panel is hidden
 
 static SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore
 static int ui_selected_index ( void );
+static void ui_start_defered_icon_thread ( void );
+static void ui_stop_defered_icon_thread ( void );
 
 unsigned char ui_setup ( void ) {
 
@@ -365,6 +371,12 @@ void ui_render ( void ) {
 
   ui_context_t *c = &ui_display_context; // for convenience and shorthand
 
+  // on demand icon loading
+  static int load_visible = -1;
+  if ( load_visible == -1 ) {
+    load_visible = pnd_conf_get_as_int_d ( g_conf, "minimenu.load_visible_icons", 0 );
+  }
+
   // how many total rows do we need?
   if ( g_categorycount ) {
     icon_rows = g_categories [ ui_category ] -> refcount / c -> col_max;
@@ -378,7 +390,32 @@ void ui_render ( void ) {
 #if 1
   // if no selected app yet, select the first one
   if ( ! ui_selected && pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) ) {
+
+    // pick first visible app
     ui_selected = g_categories [ ui_category ] -> refs;
+
+    // change.. so we pick first visible if option is set .. but now we also try to restore
+    // selection to the last app selected in previous session (if there is one.)
+    char *previous_unique_id = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_app_uid" );
+
+    if ( previous_unique_id ) {
+
+      // 1) we should already be in the right category, since its set in ui_post_scan to minimenu.last_known_catname
+      // 2) so we just pick the app in question..
+      mm_appref_t *previter = g_categories [ ui_category ] -> refs;
+      while ( previter ) {
+       if ( strcmp ( previter -> ref -> unique_id, previous_unique_id ) == 0 ) {
+         break;
+       }
+       previter = previter -> next;
+      }
+
+      if ( previter ) {
+       ui_selected = previter;
+      }
+
+    } // last known app?
+
   }
 #endif
 
@@ -714,6 +751,44 @@ void ui_render ( void ) {
            // show icon
            mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id );
            SDL_Surface *iconsurface;
+
+           // if icon not in cache, and its a pnd-file source, perhaps try to load it right now..
+           if ( ( ! ic ) &&
+                ( load_visible ) &&
+                ( ! ( appiter -> ref -> object_flags & PND_DISCO_GENERATED ) )
+              )
+           {
+             // try to load any icons that..
+             // - are not yet loaded
+             // - did not fail a previous load attempt
+             // this way user can upfront load all icons, or defer all icons, or even defer all icons
+             // and still try to load visible ones 'just before needed'; so not at mmenu load time, but
+             // as needed (only the ones needed.)
+
+             if ( ( appiter -> ref -> pnd_icon_pos ) ||
+                  ( appiter -> ref -> icon && appiter -> ref -> object_flags & PND_DISCO_LIBPND_DD )
+                )
+             {
+  
+               // try to cache it?
+               if ( ! cache_icon ( appiter -> ref, ui_display_context.icon_max_width, ui_display_context.icon_max_width ) ) {
+                 // erm..
+               }
+
+               // avoid churn
+               appiter -> ref -> pnd_icon_pos = 0;
+               if ( appiter -> ref -> icon ) {
+                 free ( appiter -> ref -> icon );
+                 appiter -> ref -> icon = NULL;
+               }
+
+               // pick up as if nothing happened..
+               ic = cache_query_icon ( appiter -> ref -> unique_id );
+
+             }
+
+           } // load icon during rendering?
+
            if ( ic ) {
              iconsurface = ic -> i;
            } else {
@@ -1388,6 +1463,8 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
          // do nothing
          ui_revealscreen();
        } else if ( sel == 1 ) {
+         // store conf on exit, so that last app/cat can be cached.. for ED :)
+         conf_write ( g_conf, conf_determine_location ( g_conf ) );
          // shutdown
          sprintf ( buffer, "sudo poweroff" );
          system ( buffer );
@@ -1395,6 +1472,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
          // configure mm
          unsigned char restart = conf_run_menu ( NULL );
          conf_write ( g_conf, conf_determine_location ( g_conf ) );
+         conf_write ( g_conf, CONF_PREF_TEMPPATH );
          if ( restart ) {
            emit_and_quit ( MM_RESTART );
          }
@@ -1789,6 +1867,28 @@ void ui_push_exec ( void ) {
     return;
   }
 
+  // cache the category/app so we can come back to it another time
+  if ( ui_selected ) {
+    pnd_conf_set_char ( g_conf, "minimenu.last_known_app_uid", ui_selected -> ref -> unique_id );
+  }
+  if ( g_categories [ 0 ] ) {
+    pnd_conf_set_char ( g_conf, "minimenu.last_known_catname", g_categories [ ui_category ] -> catname );
+
+    // and also the parent cat..
+    if ( g_categories [ ui_category ] -> parent_catname ) {
+      pnd_conf_set_char ( g_conf, "minimenu.last_known_parentcatname", g_categories [ ui_category ] -> parent_catname );
+    } else {
+      char *kv = pnd_box_find_by_key ( g_conf, "minimenu.last_known_parentcatname" );
+      if ( kv ) {
+       pnd_box_delete_node ( g_conf, kv );
+      }
+
+    }
+  }
+
+  // cache last known cat/app to /tmp, so we can use it again later
+  conf_write ( g_conf, CONF_PREF_TEMPPATH );
+
   // was this icon generated from filesystem, or from pnd-file?
   if ( ui_selected -> ref -> object_flags & PND_DISCO_GENERATED ) {
 
@@ -1986,6 +2086,10 @@ void ui_push_ltrigger ( void ) {
     return;
   }
 
+  if ( g_icon_thread_busy ) {
+    ui_stop_defered_icon_thread();
+  }
+
   if ( ui_category > 0 ) {
     ui_category--;
     category_fs_restock ( g_categories [ ui_category ] );
@@ -2014,6 +2118,7 @@ void ui_push_ltrigger ( void ) {
   ui_rows_scrolled_down = 0;
 
   render_mask |= CHANGED_CATEGORY;
+  ui_start_defered_icon_thread();
 
   return;
 }
@@ -2025,6 +2130,10 @@ void ui_push_rtrigger ( void ) {
     return;
   }
 
+  if ( g_icon_thread_busy ) {
+    ui_stop_defered_icon_thread();
+  }
+
   unsigned int screen_width = ui_display_context.screen_width;
   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
 
@@ -2053,6 +2162,7 @@ void ui_push_rtrigger ( void ) {
   ui_rows_scrolled_down = 0;
 
   render_mask |= CHANGED_CATEGORY;
+  ui_start_defered_icon_thread();
 
   return;
 }
@@ -2259,6 +2369,9 @@ void ui_set_selected ( mm_appref_t *r ) {
 
   render_mask |= CHANGED_SELECTION;
 
+  // preview pic stuff
+  //
+
   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
     return; // no desire to defer anything
   }
@@ -2620,6 +2733,7 @@ void ui_touch_act ( unsigned int x, unsigned int y ) {
        render_mask |= CHANGED_CATEGORY;
        // rescan the dir
        category_fs_restock ( g_categories [ ui_category ] );
+       ui_start_defered_icon_thread();
       }
 
       break;
@@ -2673,7 +2787,8 @@ int ui_threaded_timer ( pnd_disco_t *p ) {
   while ( 1 ) {
 
     // pause...
-    sleep ( delay_s );
+    //sleep ( delay_s );
+    SDL_Delay ( delay_s * 1000 );
 
     // .. trigger SD check
     SDL_Event e;
@@ -2708,20 +2823,8 @@ unsigned char ui_threaded_defered_preview ( pnd_disco_t *p ) {
   return ( 0 );
 }
 
-SDL_Thread *g_icon_thread = NULL;
 void ui_post_scan ( void ) {
 
-  // if deferred icon load, kick off the thread now
-  if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) == 1 ) {
-
-    g_icon_thread = SDL_CreateThread ( (void*)ui_threaded_defered_icon, NULL );
-
-    if ( ! g_icon_thread ) {
-      pnd_log ( pndn_error, "ERROR: Couldn't create icon thread\n" );
-    }
-
-  } // deferred icon load
-
   // reset view
   ui_selected = NULL;
   ui_rows_scrolled_down = 0;
@@ -2729,29 +2832,60 @@ void ui_post_scan ( void ) {
   ui_category = 0;
   ui_catshift = 0;
 
-  // do we have a preferred category to jump to?
+  // do we have a preferred category to jump to? or a last known one?
   char *dc = pnd_conf_get_as_char ( g_conf, "categories.default_cat" );
-  if ( dc ) {
+  char *lastcat = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_catname" );
+  if ( ( dc ) ||
+       ( pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) && lastcat ) )
+  {
+    char *catpick = NULL;
+
+    if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) && lastcat ) {
+      catpick = lastcat;
+
+      // if this is a subcat, we have some doctoring to do :/ the hackishness is really
+      // starting to show..
+      if ( pnd_conf_get_as_char ( g_conf, "minimenu.last_known_parentcatname" ) ) {
+       // in subcat view, we only have one cat
+       ui_category = 0;
+       ui_catshift = 0;
+       // the cat name to search for is Child*Parent
+       char key [ 512 ];
+       sprintf ( key, "%s*%s",
+                 pnd_conf_get_as_char ( g_conf, "minimenu.last_known_catname" )
+                 , pnd_conf_get_as_char ( g_conf, "minimenu.last_known_parentcatname" ) );
+       category_publish ( CFBYNAME, key );
+       // since we forced it by hand, no need to do a cat-scan below
+       catpick = NULL;
+      }
+
+    } else if ( dc ) {
+      catpick = dc;
+    }
 
     // attempt to find default cat; if we do find it, select it; otherwise
     // default behaviour will pick first cat (ie: usually All)
-    unsigned int i;
-    for ( i = 0; i < g_categorycount; i++ ) {
-      if ( strcasecmp ( g_categories [ i ] -> catname, dc ) == 0 ) {
-       ui_category = i;
-       // ensure visibility
-       unsigned int screen_width = ui_display_context.screen_width;
-       unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
-       if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
-         ui_catshift = ui_category - ( screen_width / tab_width ) + 1;
+    if ( catpick ) {
+      unsigned int i;
+
+      for ( i = 0; i < g_categorycount; i++ ) {
+       if ( strcasecmp ( g_categories [ i ] -> catname, catpick ) == 0 ) {
+         ui_category = i;
+         // ensure visibility
+         unsigned int screen_width = ui_display_context.screen_width;
+         unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
+         if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
+           ui_catshift = ui_category - ( screen_width / tab_width ) + 1;
+         }
+         break;
        }
-       break;
       }
-    }
 
-    if ( i == g_categorycount ) {
-      pnd_log ( pndn_warning, "  User defined default category '%s' but not found, so using default behaviour\n", dc );
-    }
+      if ( i == g_categorycount ) {
+       pnd_log ( pndn_warning, "  User defined default category or last known cat '%s' but not found, so using default behaviour\n", catpick );
+      }
+
+    } // cat change?
 
   } // default cat
 
@@ -2764,43 +2898,72 @@ void ui_post_scan ( void ) {
   // redraw all
   render_mask |= CHANGED_EVERYTHING;
 
+  // if deferred icon load, kick off the thread now
+  if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) == 1 ) {
+    ui_start_defered_icon_thread();
+  } // deferred icon load
+
   return;
 }
 
 unsigned char ui_threaded_defered_icon ( void *p ) {
   extern pnd_box_handle g_active_apps;
-  pnd_box_handle h = g_active_apps;
 
   unsigned char maxwidth, maxheight;
   maxwidth = pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 );
   maxheight = pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 );
 
-  pnd_disco_t *iter = pnd_box_get_head ( h );
+  pnd_disco_t *iter;
+
+  g_icon_thread_busy = 1;
 
-  while ( iter ) {
+  // work at it in order within current category
 
-    // cache it
-    if ( iter -> pnd_icon_pos &&
-        ! cache_icon ( iter, maxwidth, maxheight ) )
+  mm_appref_t *refiter = g_categories [ ui_category ] -> refs;
+  while ( refiter && ! g_icon_thread_stop ) {
+    iter = refiter -> ref;
+
+    // has an icon that is not already cached?
+    if ( ( iter -> pnd_icon_pos ) ||
+        ( iter -> icon && iter -> object_flags & PND_DISCO_LIBPND_DD )
+       )
     {
-      pnd_log ( pndn_warning, "  Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") );
-    } else {
+  
+      // try to cache it?
+      if ( ! cache_icon ( iter, maxwidth, maxheight ) ) {
+       //pnd_log ( pndn_warning, "  Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") );
 
-      // trigger that we completed
-      SDL_Event e;
-      bzero ( &e, sizeof(SDL_Event) );
-      e.type = SDL_USEREVENT;
-      e.user.code = sdl_user_finishedicon;
-      SDL_PushEvent ( &e );
+      } else {
 
-      //pnd_log ( pndn_warning, "  Finished deferred load icon: '%s'\n", IFNULL(iter->title_en,"No Name") );
-      usleep ( pnd_conf_get_as_int_d ( g_conf, "minimenu.defer_icon_us", 50000 ) );
+       // trigger that we completed
+       SDL_Event e;
+       bzero ( &e, sizeof(SDL_Event) );
+       e.type = SDL_USEREVENT;
+       e.user.code = sdl_user_finishedicon;
+       SDL_PushEvent ( &e );
 
-    }
+       //pnd_log ( pndn_warning, "  Finished deferred load icon: '%s'\n", IFNULL(iter->title_en,"No Name") );
+       //usleep ( pnd_conf_get_as_int_d ( g_conf, "minimenu.defer_icon_us", 50000 ) );
 
-    // next
-    iter = pnd_box_get_next ( iter );
-  } // while
+      }
+
+      // avoid churn
+      iter -> pnd_icon_pos = 0;
+      if ( iter -> icon ) {
+       free ( iter -> icon );
+       iter -> icon = NULL;
+      }
+
+      // let user do something..
+      SDL_Delay ( 200 );
+
+    } // has icon
+
+    refiter = refiter -> next;
+  }
+
+  // mark as done
+  g_icon_thread_busy = 0;
 
   return ( 0 );
 }
@@ -3184,6 +3347,7 @@ void ui_revealscreen ( void ) {
 
   // redraw tabs
   render_mask |= CHANGED_CATEGORY;
+  ui_start_defered_icon_thread();
 
   return;
 }
@@ -3399,6 +3563,7 @@ void ui_menu_context ( mm_appref_t *a ) {
 
          // write conf, so it will take next time
          conf_write ( g_conf, conf_determine_location ( g_conf ) );
+         conf_write ( g_conf, CONF_PREF_TEMPPATH );
 
          // can we just 'hide' this guy without reloading all apps? (this is for you, EvilDragon)
          if ( 0 ) {
@@ -3434,7 +3599,7 @@ void ui_menu_context ( mm_appref_t *a ) {
          context_alive = 0; // nolonger visible, so lets just get out
 
        }
-    
+
        break;
 
       case context_app_recategorize:
@@ -3862,7 +4027,7 @@ unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialva
     } // while waiting for input
 
   } // while
-  
+
   return ( 0 );
 }
 
@@ -4230,3 +4395,40 @@ char *ui_pick_custom_category ( unsigned char mode ) {
 
   return ( foo );
 }
+
+void ui_start_defered_icon_thread ( void ) {
+
+  if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) != 1 ) {
+    return;
+  }
+
+  if ( g_icon_thread_busy ) {
+    //fprintf ( stderr, "REM: Waiting for thread to stop..\n" );
+    ui_stop_defered_icon_thread();
+  }
+
+  //fprintf ( stderr, "WARN: Starting new icon caching thread..\n" );
+  g_icon_thread = SDL_CreateThread ( (void*)ui_threaded_defered_icon, NULL );
+
+  if ( ! g_icon_thread ) {
+    pnd_log ( pndn_error, "ERROR: Couldn't create icon thread\n" );
+  }
+
+  return;
+}
+
+void ui_stop_defered_icon_thread ( void ) {
+  time_t started = time ( NULL );
+
+  // ask thread to stop, then wait for it (if two run at same time, or if we change
+  // category content under neath it, could be bad..)
+  g_icon_thread_stop = 1;
+  while ( g_icon_thread_busy ) {
+    time ( NULL ); // spin
+  }
+  g_icon_thread_stop = 0;
+
+  fprintf ( stderr, "REM: Thread stoppage took %u seconds.\n", (int) ( time ( NULL ) - started ) );
+
+  return;
+}